netizen-cli 0.12.2__tar.gz → 0.13.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.
- {netizen_cli-0.12.2/netizen_cli.egg-info → netizen_cli-0.13.0}/PKG-INFO +1 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/README.md +8 -3
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/config.example.yaml +8 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/__init__.py +1 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/port_config.py +22 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/admin.css +8 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/admin.js +91 -33
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/index.html +30 -0
- netizen_cli-0.13.0/netizen_cli/admin/static/worktrees.js +227 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/web.py +163 -6
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/bindings.py +104 -6
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/controls.py +213 -29
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel_app.py +159 -64
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/codex_runtime.py +291 -21
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/database_migrations.py +3 -3
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/domain.py +3 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/main.py +10 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/service.py +95 -1
- netizen_cli-0.13.0/netizen_cli/migrations/v15.py +110 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/runtime/contracts.py +3 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/mcp.py +7 -5
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/policy.py +7 -13
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/presentation.py +1 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/schema.py +23 -2
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/service.py +63 -8
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/settings.py +7 -0
- netizen_cli-0.13.0/netizen_cli/worktrees/errors.py +11 -0
- netizen_cli-0.13.0/netizen_cli/worktrees/git.py +569 -0
- netizen_cli-0.13.0/netizen_cli/worktrees/models.py +56 -0
- netizen_cli-0.13.0/netizen_cli/worktrees/service.py +516 -0
- netizen_cli-0.13.0/netizen_cli/worktrees/settings.py +93 -0
- netizen_cli-0.13.0/netizen_cli/worktrees/store.py +371 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0/netizen_cli.egg-info}/PKG-INFO +1 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli.egg-info/SOURCES.txt +17 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/pyproject.toml +1 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/admin.md +36 -4
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/commands.md +2 -2
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/concepts.md +4 -3
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/feedback-and-files.md +2 -2
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/fork-side-goal.md +3 -2
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/native-capabilities.md +4 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/session-collaboration.md +15 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/sessions.md +30 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/user-guide.md +2 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_bindings.py +1 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cards.py +81 -10
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_channel_app.py +21 -6
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_data.py +52 -36
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_codex_runtime.py +13 -5
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_completion_mentions.py +3 -3
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_database_migrations.py +9 -5
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_deployment.py +2 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_goal_reply_separation.py +3 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_migration_schema_validation.py +4 -2
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_partial_answer_delivery.py +1 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_scheduled_tasks.py +9 -1
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_scheduled_channel.py +4 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_external_runtime.py +48 -2
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_tools.py +145 -0
- netizen_cli-0.13.0/tests/test_worktree_channel_cwd.py +308 -0
- netizen_cli-0.13.0/tests/test_worktree_git.py +444 -0
- netizen_cli-0.13.0/tests/test_worktree_new.py +293 -0
- netizen_cli-0.13.0/tests/test_worktree_runtime_cwd.py +724 -0
- netizen_cli-0.13.0/tests/test_worktree_service.py +493 -0
- netizen_cli-0.13.0/tests/test_worktree_settings.py +127 -0
- netizen_cli-0.13.0/tests/test_worktree_storage.py +436 -0
- netizen_cli-0.13.0/tests/test_worktree_tools_integration.py +165 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/MANIFEST.in +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/_build.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/__main__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/account_rate_limits.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/auth.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/credentials.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/errors.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/feishu.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/login.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/presentation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/queries.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/chat-picker.css +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/chat-picker.js +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/identity.js +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/login.html +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/login.js +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/static/session-access.js +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/transport.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/admin/users.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/builtin_skills.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/callbacks.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/chat_target.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/defaults.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/fork.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/model_info.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/pagination.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/questions.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/reply.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/scheduled.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/session_ids.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/status.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cards/usage.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/completion_mentions.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/input_preparation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/messages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/ports.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/question_inputs.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/reactions.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/reply_presenter.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/session_pages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/channel/topics.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/chat_avatars.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/chat_targets.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_data.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_packages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_services.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_setup.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_update.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_update_restore.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/cli_update_worker.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/completion_mention.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/defaults/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/defaults/models.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/defaults/service.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/defaults/store.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/deployment/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/deployment/restart_worker.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/deployment/update_executor.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/deployment/update_protocol.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/error_messages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/experience.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/feishu_app_onboarding.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/feishu_app_permissions.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/feishu_links.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/git_status.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/image_inputs.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/instance.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/lark_app.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/blocking_io.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/chat_directory.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/chat_labels.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/coordination.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/management/updates.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/markdown_images.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/mcp_transport.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/message_content.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/message_history.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/message_preparation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/message_projection.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/migrations/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/migrations/schema.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/migrations/v14.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/model_settings.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/package_resources.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/pagination.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/partial_answers.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/projects.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/prompt_projection.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/quoted_context.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/result_images.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/runtime/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/runtime/name_writes.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/runtime/thread_naming.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/schedules/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/schedules/mcp.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/schedules/models.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/schedules/scheduler.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/schedules/service.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/schedules/store.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sdk_gap_adapter.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/service_launcher.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/session_inventory.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/session_settings.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/__init__.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/delegation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/sessions/errors.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/skill_references.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/task_text.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/terminal_cleanup.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/thread_history.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/turn_activity.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/turn_files.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/turn_patch_children.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/turn_plan_observer.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli/user_questions.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli.egg-info/dependency_links.txt +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli.egg-info/entry_points.txt +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli.egg-info/requires.txt +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/netizen_cli.egg-info/top_level.txt +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/setup.cfg +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-lark/SKILL.md +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/SKILL.md +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/faq.md +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/messages.md +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/scheduled-tasks.md +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/skills/netizen-user-guide/references/status-and-control.md +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_account_rate_limits.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_admin_update_executor_probe.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_binding_scheduled_channel.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_channel_fixtures.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_channel_input_preparation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_chat_avatars.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_chat_target_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_check_sdk.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_distribution.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_integration.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_packages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_packaging.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_services.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_setup.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cli_update.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_codex_sdk_capabilities.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_codex_sdk_upgrade_0161.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_codex_sdk_upgrade_0163.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_completion_mention_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_completion_topic_delivery.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_cron_chat_targets.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_defaults_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_defaults_channel.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_defaults_runtime.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_documentation_links.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_domain_experience.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_error_messages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_feishu_app_onboarding.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_feishu_app_permissions.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_file_pagination.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_fork_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_git_status.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_image_inputs.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_lark_app.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_lark_sdk_contract.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_main.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_markdown_images.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_message_content.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_message_history.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_message_material_inputs.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_message_preparation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_model_info_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_model_settings.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_name_writes.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_pagination.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_partial_answers.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_persistent_fork_bindings.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_persistent_fork_channel.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_persistent_fork_runtime.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_child_files.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_feishu_completion_mention.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_feishu_partial_answers.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_feishu_turn_file_card.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_persistent_fork.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_python_sdk.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_sdk_completion_race.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_sdk_response_recovery.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_skill_execution.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_probe_thread_naming.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_project_delete_channel.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_project_delete_runtime.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_project_deletion_storage.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_prompt_projection.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_question_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_question_channel.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_question_target_parity.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_quoted_context.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_release_script.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_repository_hygiene.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_result_images.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_resume_channel.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_retired_deployment.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_runtime_resume_activation.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_runtime_thread_naming.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedule_card_retry.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedule_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedule_mcp.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedule_run_now.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedule_scope_matrix.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedule_service.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_scheduled_runtime.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_scheduler.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_scheduler_presenter.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_schedules_storage.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_sdk_gap_adapter.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_service_launcher.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_card_pages.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_collaboration_app.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_defaults.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_id_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_policy.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_session_settings.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_settings_projects.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_status_cards.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_status_commands.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_task_text.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_terminal_cleanup.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_thread_history.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_thread_naming.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_turn_activity.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_turn_files.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_turn_patch_children.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_turn_patch_statistics.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_turn_plan_observer.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_update_executor.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_usage_card.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_usage_control.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_user_guide_skill.py +0 -0
- {netizen_cli-0.12.2 → netizen_cli-0.13.0}/tests/test_user_questions.py +0 -0
|
@@ -103,7 +103,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
|
|
|
103
103
|
### 3. 在飞书开始第一次对话
|
|
104
104
|
|
|
105
105
|
1. 打开机器人单聊;如需在群里使用,先将机器人加入目标群。
|
|
106
|
-
2. 发送 `/settings
|
|
106
|
+
2. 发送 `/settings`,创建或登记一个项目并启用。项目登记一个默认工作目录。
|
|
107
107
|
3. 发送 `/new`,在卡片中选择项目。模型配置可选择“继承 Codex”;过程卡默认开启,可按需关闭。
|
|
108
108
|
4. 直接发送任务,例如:**“梳理这个项目的结构,并生成一份入门说明。”**
|
|
109
109
|
5. 收到回复后继续交流;如果出现“本轮文件”,点击“发送”取回需要的文件。
|
|
@@ -116,6 +116,11 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
|
|
|
116
116
|
群聊和群话题中的每条请求都需要重新 **@机器人**,包括命令。`/new` 只通过卡片创建会话,
|
|
117
117
|
不接受参数。普通任务执行中发来的新消息用于调整当前任务,不会排队成下一个任务。
|
|
118
118
|
|
|
119
|
+
Git 项目可在 `/new` 中加载项目目录选项,选择已有受管 worktree 或新建 worktree;默认
|
|
120
|
+
仍使用项目目录。新建只检出提交内容,不复制本地改动或执行环境初始化。
|
|
121
|
+
配置、状态查询与删除在 [Admin Worktrees](skills/netizen-user-guide/references/admin.md#worktree-配置与管理)
|
|
122
|
+
中完成;不同会话可以共享同一工作区。`/fork` 不提供新建或选择 worktree 的入口。
|
|
123
|
+
|
|
119
124
|
安装遇到问题可从[部署文档](docs/deployment.md)继续;已能对话时,常见疑问见
|
|
120
125
|
[使用 FAQ](skills/netizen-user-guide/references/faq.md#常见问题)。
|
|
121
126
|
|
|
@@ -126,7 +131,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
|
|
|
126
131
|
| 想做什么 | 入口 |
|
|
127
132
|
| --- | --- |
|
|
128
133
|
| 创建或登记项目工作目录 | `/settings` |
|
|
129
|
-
|
|
|
134
|
+
| 新建会话,选择项目及受管 worktree | `/new` |
|
|
130
135
|
| 查看、保存或删除当前聊天的会话默认配置 | `/defaults` |
|
|
131
136
|
| 查看并切换已有会话 | `/sessions` |
|
|
132
137
|
| 从当前空闲会话另建持久分支话题 | `/fork` |
|
|
@@ -149,7 +154,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
|
|
|
149
154
|
/side 这个方案还有哪些边界情况需要考虑?
|
|
150
155
|
```
|
|
151
156
|
|
|
152
|
-
Side
|
|
157
|
+
Side 会在同一聊天中新建话题,可继续多轮讨论。它与原会话共享实际工作目录,空闲两小时或
|
|
153
158
|
服务重启后过期;`/side close` 主动结束话题会话。Side 中不支持 Goal。
|
|
154
159
|
|
|
155
160
|
**每天回顾项目进展。** 在已经选择项目的普通会话中发送:
|
|
@@ -9,9 +9,16 @@ instance:
|
|
|
9
9
|
# User and chat availability are configured in the Feishu app console. Netizen
|
|
10
10
|
# does not maintain a second allowlist.
|
|
11
11
|
projects:
|
|
12
|
-
#
|
|
12
|
+
# Default cwd. /new can explicitly select a managed Worktree of this Project.
|
|
13
13
|
test: /home/your-user/projects/test
|
|
14
14
|
|
|
15
|
+
# Instance-owned Git worktrees; Admin can change these settings for future creates.
|
|
16
|
+
# Omitted root uses <instance-root>/worktrees; existing checkouts are not moved.
|
|
17
|
+
# No Codex App global configuration or private managed index is modified.
|
|
18
|
+
worktrees:
|
|
19
|
+
# root: /home/your-user/.netizen/worktrees
|
|
20
|
+
fetchBeforeCreate: false
|
|
21
|
+
|
|
15
22
|
channel:
|
|
16
23
|
securityMode: audit
|
|
17
24
|
|
|
@@ -6,6 +6,7 @@ import contextlib
|
|
|
6
6
|
import os
|
|
7
7
|
import stat
|
|
8
8
|
import tempfile
|
|
9
|
+
import threading
|
|
9
10
|
from dataclasses import dataclass, field
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
@@ -45,6 +46,27 @@ class ConfigFileSnapshot:
|
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
class ConfigFileEditor:
|
|
50
|
+
"""Serialize instance-owned section edits against one exact file snapshot.
|
|
51
|
+
|
|
52
|
+
Settings services share this object so their successful writes do not make
|
|
53
|
+
each other stale. External edits still conflict with the complete snapshot;
|
|
54
|
+
they are never silently merged or made effective in the running service.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, snapshot: ConfigFileSnapshot) -> None:
|
|
58
|
+
self._snapshot = snapshot
|
|
59
|
+
self._lock = threading.RLock()
|
|
60
|
+
|
|
61
|
+
def update_section(self, name: str, value: object) -> None:
|
|
62
|
+
with self._lock:
|
|
63
|
+
loaded = yaml.safe_load(self._snapshot.content)
|
|
64
|
+
if not isinstance(loaded, dict):
|
|
65
|
+
raise AdminPortConfigurationError("configuration root must be a mapping")
|
|
66
|
+
loaded[name] = value
|
|
67
|
+
self._snapshot = write_configuration(self._snapshot, loaded)
|
|
68
|
+
|
|
69
|
+
|
|
48
70
|
def _identity(metadata: os.stat_result) -> tuple[int, ...]:
|
|
49
71
|
return (
|
|
50
72
|
metadata.st_dev, metadata.st_ino, metadata.st_mode, metadata.st_size,
|
|
@@ -101,7 +101,8 @@ input, select, textarea { min-width: 0; border: 1px solid #bac3d0; border-radius
|
|
|
101
101
|
.time-range-calendar-icon { position: relative; width: 15px; height: 14px; flex: 0 0 auto; border: 1.5px solid currentColor; border-radius: 3px; color: var(--muted); }
|
|
102
102
|
.time-range-calendar-icon::before { content: ""; position: absolute; left: -1.5px; right: -1.5px; top: 3px; border-top: 1.5px solid currentColor; }
|
|
103
103
|
.time-range-calendar-icon::after { content: ""; position: absolute; width: 2px; height: 2px; left: 3px; top: 7px; border-radius: 50%; background: currentColor; box-shadow: 4px 0 currentColor, 8px 0 currentColor; }
|
|
104
|
-
.time-range-summary { min-width: 0; flex: 1;
|
|
104
|
+
.time-range-summary { min-width: 0; flex: 1; white-space: pre-line; overflow-wrap: anywhere; }
|
|
105
|
+
.time-range-dates { color: var(--muted); font-size: 11px; }
|
|
105
106
|
.time-range-chevron { flex: 0 0 auto; color: var(--muted); font-size: 16px; line-height: 1; }
|
|
106
107
|
.time-range-quick-clear { position: absolute; z-index: 1; top: 50%; right: 30px; width: 24px; height: 24px; transform: translateY(-50%); border: 0; border-radius: 50%; padding: 0; background: transparent; color: var(--muted); font-size: 18px; line-height: 22px; }
|
|
107
108
|
.time-range-quick-clear:hover { background: #edf1f6; color: var(--ink); }
|
|
@@ -160,6 +161,12 @@ tr:last-child td { border-bottom: 0; }
|
|
|
160
161
|
.badge.on { background: #e5f6ed; color: #136c3c; }
|
|
161
162
|
.badge.warn { background: #fff2df; color: #925400; }
|
|
162
163
|
.actions { display: flex; flex-wrap: wrap; gap: 5px; }
|
|
164
|
+
.cascade-choice, .card-form label.cascade-choice { display: flex; align-items: center; gap: 6px; font-size: 12px; }
|
|
165
|
+
.cascade-choice input, .card-form .cascade-choice input { width: auto; margin: 0; }
|
|
166
|
+
#worktrees-body td { white-space: pre-line; overflow-wrap: anywhere; }
|
|
167
|
+
#worktrees-body td:last-child { min-width: 170px; }
|
|
168
|
+
#worktrees-body .action { margin-top: 8px; }
|
|
169
|
+
#worktree-result { white-space: pre-line; }
|
|
163
170
|
.project-delete-result { margin: 16px 0; padding: 14px 16px; border: 1px solid var(--line); border-radius: 9px; background: var(--wash); font-size: 13px; line-height: 1.6; }
|
|
164
171
|
.project-delete-result.error { border-color: #e3b8b4; color: var(--danger); }
|
|
165
172
|
.project-delete-result p { margin: 0; white-space: pre-line; }
|
|
@@ -62,6 +62,7 @@ function expireAdminSession() {
|
|
|
62
62
|
if (notice) notice.hidden = false;
|
|
63
63
|
window.NetizenIdentity?.expire();
|
|
64
64
|
window.NetizenSessionAccess?.expire();
|
|
65
|
+
window.NetizenWorktrees?.expire();
|
|
65
66
|
setStatus("登录已过期,请重新登录。", true);
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -444,11 +445,13 @@ function confirmProjectDelete(preview) {
|
|
|
444
445
|
+ "\n定时计划将被删除;后续会话清理失败也不会恢复计划。"
|
|
445
446
|
+ "\n\n原生会话、派生子会话、Codex App/CLI 历史和本地会话登记将永久删除,无法恢复。"
|
|
446
447
|
+ "\n全部会话删除确认成功后,才会删除 Project 登记;部分失败时会保留 Project 和剩余会话。"
|
|
448
|
+
+ `\n关联受管 Worktrees:${preview.worktreeCount || 0},${preview.deleteWorktrees ? "同时安全删除;失败项保留,不强制丢弃成果" : "保留目录,可在 Worktrees 页面处理;不会自动关联到同名新 Project"}。`
|
|
449
|
+
+ (preview.deleteWorktrees && preview.worktrees?.length ? `\n将删除的 checkout 根目录:\n${preview.worktrees.map((item) => `${item.id} · ${item.root}`).join("\n")}` : "")
|
|
447
450
|
+ `\n\n磁盘代码目录保留:${preview.project.cwd}`,
|
|
448
451
|
);
|
|
449
452
|
}
|
|
450
453
|
|
|
451
|
-
async function deleteProject(project) {
|
|
454
|
+
async function deleteProject(project, deleteWorktrees = false) {
|
|
452
455
|
const envelope = project.actions.previewDelete;
|
|
453
456
|
if (!envelope || pendingProjectDeletes.has(project.alias)) return;
|
|
454
457
|
pendingProjectDeletes.add(project.alias);
|
|
@@ -467,7 +470,7 @@ async function deleteProject(project) {
|
|
|
467
470
|
const preview = await api("/api/v1/projects/delete-preview", {
|
|
468
471
|
method: "POST",
|
|
469
472
|
headers: { "Content-Type": "application/json" },
|
|
470
|
-
body: JSON.stringify(actionPayload(envelope)),
|
|
473
|
+
body: JSON.stringify(actionPayload(envelope, deleteWorktrees ? { deleteWorktrees: true } : {})),
|
|
471
474
|
});
|
|
472
475
|
if (!confirmProjectDelete(preview)) {
|
|
473
476
|
message = `已取消删除 Project「${project.alias}」,未提交删除。`;
|
|
@@ -498,6 +501,11 @@ async function deleteProject(project) {
|
|
|
498
501
|
if (result.remainingScheduledRunCount) {
|
|
499
502
|
details.push(`仍有 ${result.remainingScheduledRunCount} 条定时执行的交接或清理未确认。`);
|
|
500
503
|
}
|
|
504
|
+
if (result.deletedWorktreeCount) message += `\n已删除 ${result.deletedWorktreeCount} 个受管 Worktree。`;
|
|
505
|
+
if (result.remainingWorktreeCount) {
|
|
506
|
+
if (result.deleteWorktrees) details.push(`仍有 ${result.remainingWorktreeCount} 个受管 Worktree 未安全删除,请在 Worktrees 页面检查。`);
|
|
507
|
+
else message += `\n已按选择保留 ${result.remainingWorktreeCount} 个受管 Worktree,可在 Worktrees 页面处理。`;
|
|
508
|
+
}
|
|
501
509
|
return result;
|
|
502
510
|
} catch (error) {
|
|
503
511
|
isError = true;
|
|
@@ -551,8 +559,14 @@ async function loadProjects(cursor = undefined) {
|
|
|
551
559
|
));
|
|
552
560
|
}
|
|
553
561
|
if (project.actions.previewDelete) {
|
|
562
|
+
const cascadeLabel = document.createElement("label");
|
|
563
|
+
cascadeLabel.className = "cascade-choice";
|
|
564
|
+
const cascade = document.createElement("input");
|
|
565
|
+
cascade.type = "checkbox";
|
|
566
|
+
cascadeLabel.append(cascade, document.createTextNode("同时删除关联受管 Worktrees"));
|
|
567
|
+
actions.append(cascadeLabel);
|
|
554
568
|
actions.append(actionButton(
|
|
555
|
-
"删除 Project 及关联 Sessions", () => deleteProject(project), true,
|
|
569
|
+
"删除 Project 及关联 Sessions", () => deleteProject(project, cascade.checked), true,
|
|
556
570
|
));
|
|
557
571
|
}
|
|
558
572
|
if (project.deleting || pendingProjectDeletes.has(project.alias)) {
|
|
@@ -1022,6 +1036,10 @@ function formQuery(form, defaultPageSize = "25", refreshRelativeTime = true) {
|
|
|
1022
1036
|
for (const [key, value] of new FormData(form)) {
|
|
1023
1037
|
if (String(value)) query.append(key, String(value));
|
|
1024
1038
|
}
|
|
1039
|
+
for (const root of form.querySelectorAll("[data-time-range]")) {
|
|
1040
|
+
const kind = timeRangeControllers.get(root)?.presetKind();
|
|
1041
|
+
if (kind) query.set(`${root.dataset.timeRangeField || "created"}Range`, kind);
|
|
1042
|
+
}
|
|
1025
1043
|
for (const root of form.querySelectorAll("[data-session-multi], [data-side-multi]")) {
|
|
1026
1044
|
const name = root.dataset.sessionMulti || root.dataset.sideMulti;
|
|
1027
1045
|
const filters = root.dataset.sideMulti ? sideMultiFilters : sessionMultiFilters;
|
|
@@ -1034,7 +1052,7 @@ function formQuery(form, defaultPageSize = "25", refreshRelativeTime = true) {
|
|
|
1034
1052
|
const identity = new URLSearchParams(state.sessionPage?.query || "").get("identity");
|
|
1035
1053
|
if (identity) query.set("identity", identity);
|
|
1036
1054
|
if (query.get("inventoryState") === "lazy") {
|
|
1037
|
-
for (const key of ["createdFrom", "createdBefore", "updatedFrom", "updatedBefore"]) query.delete(key);
|
|
1055
|
+
for (const key of ["createdFrom", "createdBefore", "createdRange", "updatedFrom", "updatedBefore", "updatedRange"]) query.delete(key);
|
|
1038
1056
|
} else if (!query.has("updatedFrom") && !query.has("updatedBefore")) {
|
|
1039
1057
|
query.set("updatedRange", "all"); // URL intent only; never sent to the API.
|
|
1040
1058
|
}
|
|
@@ -1082,10 +1100,6 @@ function formatLocalMinute(value) {
|
|
|
1082
1100
|
return localMinuteValue(value).replace("T", " ");
|
|
1083
1101
|
}
|
|
1084
1102
|
|
|
1085
|
-
function compactLocalMinute(value) {
|
|
1086
|
-
return `${twoDigits(value.getMonth() + 1)}-${twoDigits(value.getDate())} ${twoDigits(value.getHours())}:${twoDigits(value.getMinutes())}`;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
1103
|
function utcOffsetLabel(value) {
|
|
1090
1104
|
const total = -value.getTimezoneOffset();
|
|
1091
1105
|
const sign = total >= 0 ? "+" : "-";
|
|
@@ -1182,6 +1196,24 @@ function allTimeRange() {
|
|
|
1182
1196
|
};
|
|
1183
1197
|
}
|
|
1184
1198
|
|
|
1199
|
+
function timeRangeSummary(from, before, kind = "custom", now = new Date()) {
|
|
1200
|
+
if (!from && !before) return "全部时间";
|
|
1201
|
+
const rolling = kind.startsWith("last-");
|
|
1202
|
+
const start = new Date(from);
|
|
1203
|
+
// Show the last included minute, including a partially included rolling minute.
|
|
1204
|
+
const end = before ? new Date(new Date(before).getTime() - 1) : now;
|
|
1205
|
+
const startLabel = Number.isNaN(start.getTime()) ? from || "不限起点" : formatLocalMinute(start);
|
|
1206
|
+
const endLabel = !before && !rolling ? "不限终点"
|
|
1207
|
+
: Number.isNaN(end.getTime()) ? before : formatLocalMinute(end);
|
|
1208
|
+
const label = timeRangePresetLabels[kind];
|
|
1209
|
+
const name = label && kind !== "all" ? `${label}${!before && rolling ? "(至今)" : ""}` : "";
|
|
1210
|
+
const dates = [from ? start : null, before || rolling ? end : null]
|
|
1211
|
+
.filter((value) => value && !Number.isNaN(value.getTime()));
|
|
1212
|
+
const offsets = [...new Set(dates.map(utcOffsetLabel))].join(" → ");
|
|
1213
|
+
const heading = [name, offsets].filter(Boolean).join(" · ");
|
|
1214
|
+
return `${heading ? `${heading}\n` : ""}${startLabel} ~ ${endLabel}`;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1185
1217
|
function presetTimeRange(kind, now = new Date(), openEnded = false) {
|
|
1186
1218
|
if (kind === "all") return allTimeRange();
|
|
1187
1219
|
let from;
|
|
@@ -1210,18 +1242,16 @@ function presetTimeRange(kind, now = new Date(), openEnded = false) {
|
|
|
1210
1242
|
before = new Date(now.getTime());
|
|
1211
1243
|
from = new Date(now.getTime() - durationDays * 24 * 60 * 60 * 1000);
|
|
1212
1244
|
}
|
|
1213
|
-
const label = timeRangePresetLabels[kind];
|
|
1214
1245
|
const rolling = kind.startsWith("last-");
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
: `${label} · ${utcOffsetLabel(from)}`;
|
|
1246
|
+
const fromUtc = canonicalUtc(from);
|
|
1247
|
+
const beforeUtc = openEnded && rolling ? "" : canonicalUtc(before);
|
|
1218
1248
|
return {
|
|
1219
1249
|
kind,
|
|
1220
|
-
from:
|
|
1221
|
-
before:
|
|
1250
|
+
from: fromUtc,
|
|
1251
|
+
before: beforeUtc,
|
|
1222
1252
|
startValue: localMinuteValue(from),
|
|
1223
|
-
endValue: localMinuteValue(new Date(before.getTime() -
|
|
1224
|
-
summary,
|
|
1253
|
+
endValue: localMinuteValue(new Date(before.getTime() - 1)),
|
|
1254
|
+
summary: timeRangeSummary(fromUtc, beforeUtc, kind, now),
|
|
1225
1255
|
};
|
|
1226
1256
|
}
|
|
1227
1257
|
|
|
@@ -1252,7 +1282,7 @@ function customTimeRange(startValue, endValue) {
|
|
|
1252
1282
|
before: canonicalUtc(before),
|
|
1253
1283
|
startValue,
|
|
1254
1284
|
endValue,
|
|
1255
|
-
summary:
|
|
1285
|
+
summary: timeRangeSummary(canonicalUtc(parsedStart.date), canonicalUtc(before)),
|
|
1256
1286
|
},
|
|
1257
1287
|
error: "",
|
|
1258
1288
|
invalid: null,
|
|
@@ -1318,7 +1348,19 @@ function initializeTimeRangeFilter(root) {
|
|
|
1318
1348
|
function renderApplied() {
|
|
1319
1349
|
fromNode.value = applied.from;
|
|
1320
1350
|
beforeNode.value = applied.before;
|
|
1321
|
-
summary
|
|
1351
|
+
applied.summary = timeRangeSummary(applied.from, applied.before, applied.kind);
|
|
1352
|
+
const [heading, bounds] = applied.summary.split("\n");
|
|
1353
|
+
if (bounds) {
|
|
1354
|
+
const name = document.createElement("span");
|
|
1355
|
+
name.textContent = `${heading}\n`;
|
|
1356
|
+
const dates = document.createElement("span");
|
|
1357
|
+
dates.className = "time-range-dates";
|
|
1358
|
+
dates.textContent = bounds;
|
|
1359
|
+
summary.replaceChildren(name, dates);
|
|
1360
|
+
} else summary.textContent = applied.summary;
|
|
1361
|
+
const dates = [applied.from, applied.before || new Date()].filter(Boolean)
|
|
1362
|
+
.map((value) => new Date(value)).filter((value) => !Number.isNaN(value.getTime()));
|
|
1363
|
+
trigger.title = `${applied.summary}\n${zoneDescription(dates)}`;
|
|
1322
1364
|
trigger.classList.toggle("has-value", applied.kind !== "all");
|
|
1323
1365
|
trigger.setAttribute("aria-label", `${label}:${applied.summary}`);
|
|
1324
1366
|
quickClear.hidden = applied.kind === "all";
|
|
@@ -1405,7 +1447,7 @@ function initializeTimeRangeFilter(root) {
|
|
|
1405
1447
|
}
|
|
1406
1448
|
|
|
1407
1449
|
function prepareQuery() {
|
|
1408
|
-
if (applied.kind === "all" || applied.kind === "custom") return;
|
|
1450
|
+
if (applied.kind === "all" || applied.kind === "custom" || applied.restored) return;
|
|
1409
1451
|
applied = preset(applied.kind);
|
|
1410
1452
|
renderApplied();
|
|
1411
1453
|
}
|
|
@@ -1430,15 +1472,17 @@ function initializeTimeRangeFilter(root) {
|
|
|
1430
1472
|
const controller = {
|
|
1431
1473
|
cancel: cancelDraft,
|
|
1432
1474
|
prepareQuery,
|
|
1433
|
-
|
|
1475
|
+
refreshSummary: renderApplied,
|
|
1476
|
+
presetKind() { return applied.kind !== "all" && applied.kind !== "custom" ? applied.kind : null; },
|
|
1477
|
+
setBounds(from, before, kind = "custom") {
|
|
1434
1478
|
if (!from && !before) { this.reset("all"); return; }
|
|
1435
1479
|
const startDate = new Date(from);
|
|
1436
1480
|
const beforeDate = new Date(before);
|
|
1437
|
-
const endDate = new Date(beforeDate.getTime() -
|
|
1438
|
-
|
|
1481
|
+
const endDate = new Date(beforeDate.getTime() - 1);
|
|
1482
|
+
// URL bounds stay absolute; the preset name is presentation metadata only.
|
|
1483
|
+
applied = { kind: Object.hasOwn(timeRangePresetLabels, kind) && kind !== "all" ? kind : "custom", from, before, restored: true,
|
|
1439
1484
|
startValue: Number.isNaN(startDate.getTime()) ? "" : localMinuteValue(startDate),
|
|
1440
|
-
endValue: Number.isNaN(endDate.getTime()) ? "" : localMinuteValue(endDate)
|
|
1441
|
-
summary: `${Number.isNaN(startDate.getTime()) ? from || "不限起点" : formatLocalMinute(startDate)} — ${Number.isNaN(endDate.getTime()) ? before || "不限终点" : formatLocalMinute(endDate)}` };
|
|
1485
|
+
endValue: Number.isNaN(endDate.getTime()) ? "" : localMinuteValue(endDate) };
|
|
1442
1486
|
draft = { ...applied };
|
|
1443
1487
|
closePopover({ restoreFocus: false });
|
|
1444
1488
|
renderApplied();
|
|
@@ -1836,7 +1880,7 @@ async function loadSessionPage(kind, page, render) {
|
|
|
1836
1880
|
const candidate = { ...page, query: query.toString(), previousCursors: [...page.previousCursors] };
|
|
1837
1881
|
await loadSessionProjectOptions(kind === "side" ? sideMultiFilters : sessionMultiFilters, isCurrent);
|
|
1838
1882
|
if (!isCurrent()) return false;
|
|
1839
|
-
query.delete(
|
|
1883
|
+
for (const field of ["created", "updated"]) query.delete(`${field}Range`);
|
|
1840
1884
|
if (page.cursor) query.set("cursor", page.cursor);
|
|
1841
1885
|
const data = await api(`/api/v1/${kind === "side" ? "side-topics" : "sessions"}?${query}`);
|
|
1842
1886
|
if (!isCurrent()) return false;
|
|
@@ -1849,6 +1893,9 @@ async function loadSessionPage(kind, page, render) {
|
|
|
1849
1893
|
renderSessionPagination(kind);
|
|
1850
1894
|
renderSessionSort(kind);
|
|
1851
1895
|
renderSessionFreshness();
|
|
1896
|
+
for (const root of document.querySelector(`#${kind}-filter`).querySelectorAll("[data-time-range]")) {
|
|
1897
|
+
timeRangeControllers.get(root)?.refreshSummary();
|
|
1898
|
+
}
|
|
1852
1899
|
syncPageUrl();
|
|
1853
1900
|
setStatus("已更新。");
|
|
1854
1901
|
return true;
|
|
@@ -2694,20 +2741,22 @@ function confirmDiscardChanges(changed) {
|
|
|
2694
2741
|
|
|
2695
2742
|
function hasUnsavedEditors() {
|
|
2696
2743
|
return sessionSettingsHasChanges() || defaultEditorHasChanges() || scheduleEditorHasChanges()
|
|
2697
|
-
|| Boolean(pendingScheduleDraft?.dirty) || Boolean(window.NetizenSessionAccess?.dirty())
|
|
2744
|
+
|| Boolean(pendingScheduleDraft?.dirty) || Boolean(window.NetizenSessionAccess?.dirty())
|
|
2745
|
+
|| Boolean(window.NetizenWorktrees?.dirty());
|
|
2698
2746
|
}
|
|
2699
2747
|
|
|
2700
2748
|
// Navigation asks once before closing any editor. Busy submissions retain the
|
|
2701
2749
|
// existing fail-closed boundary; this helper never cancels or repeats a POST.
|
|
2702
2750
|
function confirmDiscardEditors() {
|
|
2703
2751
|
if (sessionSettingsEditor?.saving || defaultsBusy || (scheduleEditor && scheduleInput("fields").disabled)
|
|
2704
|
-
|| window.NetizenSessionAccess?.busy()) return false;
|
|
2752
|
+
|| window.NetizenSessionAccess?.busy() || window.NetizenWorktrees?.busy()) return false;
|
|
2705
2753
|
if (!confirmDiscardChanges(hasUnsavedEditors())) return false;
|
|
2706
2754
|
closeSessionSettingsEditor({ confirmed: true });
|
|
2707
2755
|
closeDefaultEditor({ confirmed: true });
|
|
2708
2756
|
if (scheduleEditor) closeScheduleEditor({ confirmed: true });
|
|
2709
2757
|
cancelScheduleSelection({ confirmed: true });
|
|
2710
2758
|
window.NetizenSessionAccess?.discard();
|
|
2759
|
+
window.NetizenWorktrees?.discard();
|
|
2711
2760
|
return true;
|
|
2712
2761
|
}
|
|
2713
2762
|
|
|
@@ -3801,11 +3850,11 @@ async function moveScheduleRunsPage(direction) {
|
|
|
3801
3850
|
|
|
3802
3851
|
// Only durable query intent belongs in URLs. Grants, editor drafts and live
|
|
3803
3852
|
// keyset cursors stay in this document and are never restored as server facts.
|
|
3804
|
-
const adminPagePaths = { projects: "/projects", schedules: "/schedules", defaults: "/defaults",
|
|
3853
|
+
const adminPagePaths = { projects: "/projects", worktrees: "/worktrees", schedules: "/schedules", defaults: "/defaults",
|
|
3805
3854
|
updates: "/maintenance", system: "/system" };
|
|
3806
3855
|
const sessionUrlKeys = ["project", "scopeKind", "inventoryState", "current", "chatId", "topicId", "identity",
|
|
3807
|
-
"createdFrom", "createdBefore", "updatedFrom", "updatedBefore", "updatedRange", "pageSize", "sortBy", "sortDirection"];
|
|
3808
|
-
const sideUrlKeys = ["project", "state", "chatId", "createdFrom", "createdBefore", "pageSize", "sortBy", "sortDirection"];
|
|
3856
|
+
"createdFrom", "createdBefore", "createdRange", "updatedFrom", "updatedBefore", "updatedRange", "pageSize", "sortBy", "sortDirection"];
|
|
3857
|
+
const sideUrlKeys = ["project", "state", "chatId", "createdFrom", "createdBefore", "createdRange", "pageSize", "sortBy", "sortDirection"];
|
|
3809
3858
|
let navigationReady = false;
|
|
3810
3859
|
let navigationIndex = 0;
|
|
3811
3860
|
let navigationHref = "";
|
|
@@ -3826,13 +3875,20 @@ function pageQuery(tab, sessionTab, raw = "") {
|
|
|
3826
3875
|
if (!["active", "archived", "lazy"].includes(query.get(field))) query.set(field, "active");
|
|
3827
3876
|
else query.set(field, query.get(field));
|
|
3828
3877
|
if (query.get(field) === "lazy") {
|
|
3829
|
-
for (const key of ["createdFrom", "createdBefore", "updatedFrom", "updatedBefore", "updatedRange"]) query.delete(key);
|
|
3878
|
+
for (const key of ["createdFrom", "createdBefore", "createdRange", "updatedFrom", "updatedBefore", "updatedRange"]) query.delete(key);
|
|
3830
3879
|
query.set("sortBy", "createdAt");
|
|
3831
3880
|
} else if (!query.has("updatedFrom") && !query.has("updatedBefore") && query.get("updatedRange") !== "all") {
|
|
3832
3881
|
const range = presetTimeRange("last-3-months", new Date(), true);
|
|
3833
3882
|
query.set("updatedFrom", range.from);
|
|
3883
|
+
query.set("updatedRange", range.kind);
|
|
3834
3884
|
}
|
|
3835
|
-
|
|
3885
|
+
}
|
|
3886
|
+
for (const field of ["created", "updated"]) {
|
|
3887
|
+
const kind = query.get(`${field}Range`);
|
|
3888
|
+
const hasBounds = query.has(`${field}From`) || query.has(`${field}Before`);
|
|
3889
|
+
const valid = Object.hasOwn(timeRangePresetLabels, kind) && (kind === "all" ? !hasBounds : hasBounds);
|
|
3890
|
+
if (valid) query.set(`${field}Range`, kind);
|
|
3891
|
+
else query.delete(`${field}Range`);
|
|
3836
3892
|
}
|
|
3837
3893
|
if (!query.has("pageSize")) query.set("pageSize", "20");
|
|
3838
3894
|
if (!query.has("sortBy")) query.set("sortBy", "updatedAt");
|
|
@@ -3915,7 +3971,7 @@ function restorePageFilters(route) {
|
|
|
3915
3971
|
document.querySelector(`#${kind}-page-size`).value = query.get("pageSize");
|
|
3916
3972
|
for (const root of document.querySelector(`#${kind}-filter`).querySelectorAll("[data-time-range]")) {
|
|
3917
3973
|
const field = root.dataset.timeRangeField || "created";
|
|
3918
|
-
timeRangeControllers.get(root).setBounds(query.get(`${field}From`) || "", query.get(`${field}Before`) || "");
|
|
3974
|
+
timeRangeControllers.get(root).setBounds(query.get(`${field}From`) || "", query.get(`${field}Before`) || "", query.get(`${field}Range`) || "custom");
|
|
3919
3975
|
}
|
|
3920
3976
|
if (kind === "session") {
|
|
3921
3977
|
document.querySelector("#session-inventory-state").value = query.get("inventoryState");
|
|
@@ -3993,6 +4049,7 @@ async function refresh(tab, cursor = undefined) {
|
|
|
3993
4049
|
setStatus("正在读取服务端事实…");
|
|
3994
4050
|
try {
|
|
3995
4051
|
if (tab === "projects" && await loadProjects(cursor) === false) return false;
|
|
4052
|
+
if (tab === "worktrees" && await window.NetizenWorktrees.load() === false) return false;
|
|
3996
4053
|
if (tab === "updates") await loadUpdates();
|
|
3997
4054
|
if (tab === "schedules" && await loadSchedules(cursor) === false) return false;
|
|
3998
4055
|
if (tab === "defaults") await loadDefaults();
|
|
@@ -4126,6 +4183,7 @@ document.querySelector("#side-page-size").addEventListener("change", () => {
|
|
|
4126
4183
|
});
|
|
4127
4184
|
document.querySelector("#projects-previous").addEventListener("click", () => moveListPage("projects", "previous"));
|
|
4128
4185
|
document.querySelector("#projects-next").addEventListener("click", () => moveListPage("projects", "next"));
|
|
4186
|
+
window.NetizenWorktrees?.init({ api, actionPayload, setStatus, queryProjectOptions, isExpired: () => state.authExpired });
|
|
4129
4187
|
document.querySelector("#sessions-previous").addEventListener("click", () => moveSessionPage("previous"));
|
|
4130
4188
|
document.querySelector("#sessions-next").addEventListener("click", () => moveSessionPage("next"));
|
|
4131
4189
|
document.querySelector("#sides-previous").addEventListener("click", () => moveSidePage("previous"));
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<script src="/static/chat-picker.js" defer></script>
|
|
10
10
|
<script src="/static/identity.js" defer></script>
|
|
11
11
|
<script src="/static/session-access.js" defer></script>
|
|
12
|
+
<script src="/static/worktrees.js" defer></script>
|
|
12
13
|
<script src="/static/admin.js" defer></script>
|
|
13
14
|
</head>
|
|
14
15
|
<body>
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
|
|
25
26
|
<nav class="tabs" aria-label="管理模块">
|
|
26
27
|
<a class="tab active" data-tab="projects" href="/projects" aria-current="page">Projects</a>
|
|
28
|
+
<a class="tab" data-tab="worktrees" href="/worktrees">Worktrees</a>
|
|
27
29
|
<a class="tab" data-tab="sessions" href="/sessions/ordinary">Sessions</a>
|
|
28
30
|
<a class="tab" data-tab="schedules" href="/schedules">定时任务</a>
|
|
29
31
|
<a class="tab" data-tab="defaults" href="/defaults">默认会话配置</a>
|
|
@@ -67,6 +69,34 @@
|
|
|
67
69
|
</nav>
|
|
68
70
|
</section>
|
|
69
71
|
|
|
72
|
+
<section id="worktrees" class="panel" aria-labelledby="worktrees-heading" hidden>
|
|
73
|
+
<div class="section-head"><div><p class="eyebrow">MANAGED CHECKOUTS</p><h2 id="worktrees-heading">Worktrees</h2></div><button class="refresh" data-refresh="worktrees">刷新</button></div>
|
|
74
|
+
<p class="hint">仅管理本实例创建的工作区。多个会话可共享文件和分支;列表按需查询,不扫描未提交内容,也不能证明没有外部客户端使用。</p>
|
|
75
|
+
<div class="forms-grid">
|
|
76
|
+
<form id="worktree-settings" class="card-form">
|
|
77
|
+
<h3>配置</h3>
|
|
78
|
+
<label>新建根目录(留空使用实例默认目录)<input id="worktree-root" maxlength="4096"></label>
|
|
79
|
+
<p id="worktree-effective-root" class="hint"></p>
|
|
80
|
+
<label class="cascade-choice"><input id="worktree-fetch" type="checkbox">创建前 fetch;失败则终止创建</label>
|
|
81
|
+
<p class="hint">只影响之后创建的位置,已有工作区仍按原路径管理;不修改 Codex App 配置。</p>
|
|
82
|
+
<button id="worktree-settings-save" type="submit" disabled>保存配置</button>
|
|
83
|
+
</form>
|
|
84
|
+
<form id="worktree-create" class="card-form">
|
|
85
|
+
<h3>新建 Worktree</h3>
|
|
86
|
+
<label>Project<select id="worktree-project" required><option value="">选择 Project</option></select></label>
|
|
87
|
+
<label>检出方式<select id="worktree-mode"><option value="detached">Detached(不绑定命名分支)</option><option value="existing">检出已有本地分支</option><option value="new">创建并检出新分支</option></select></label>
|
|
88
|
+
<label id="worktree-ref-label">起点提交 / 分支<input id="worktree-ref" list="worktree-refs" placeholder="HEAD"><datalist id="worktree-refs"></datalist></label>
|
|
89
|
+
<label id="worktree-existing-label" hidden>本地分支<select id="worktree-existing"></select></label>
|
|
90
|
+
<label id="worktree-new-label" hidden>新分支名称<input id="worktree-new-branch" maxlength="1024"></label>
|
|
91
|
+
<p id="worktree-create-note" class="hint">只检出已提交内容,不复制未提交、未跟踪或忽略文件,不执行环境初始化。不创建会话。</p>
|
|
92
|
+
<button id="worktree-create-submit" type="submit" disabled>创建 Worktree</button>
|
|
93
|
+
</form>
|
|
94
|
+
</div>
|
|
95
|
+
<p id="worktree-result" class="status" role="status" aria-live="polite"></p>
|
|
96
|
+
<div class="table-wrap"><table><thead><tr><th>Project / 标识</th><th>工作目录</th><th>当前状态</th><th>分支 / HEAD</th><th>关联会话</th><th>操作</th></tr></thead><tbody id="worktrees-body"></tbody></table></div>
|
|
97
|
+
<nav class="pagination" aria-label="Worktrees 分页"><button id="worktrees-previous" class="next" disabled>上一页</button><span id="worktrees-page" aria-live="polite">第 1 页</span><button id="worktrees-next" class="next" disabled>下一页</button></nav>
|
|
98
|
+
</section>
|
|
99
|
+
|
|
70
100
|
<section id="sessions" class="panel" aria-labelledby="sessions-heading" hidden>
|
|
71
101
|
<div class="section-head">
|
|
72
102
|
<div><p class="eyebrow">CONVERSATIONS</p><h2 id="sessions-heading">Sessions</h2></div>
|