@intent-systems/nexus 2026.1.5-4 → 2026.1.5-8
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.
- package/dist/agents/agent-id.js +41 -0
- package/dist/agents/auth-profiles.js +114 -25
- package/dist/agents/identity-state.js +101 -0
- package/dist/agents/model-auth.js +1 -0
- package/dist/agents/model-fallback.js +15 -9
- package/dist/agents/model-selection.js +1 -1
- package/dist/agents/models-config.js +17 -11
- package/dist/agents/pi-embedded-runner.js +101 -9
- package/dist/agents/sandbox.js +12 -3
- package/dist/agents/skill-runner.js +41 -6
- package/dist/agents/skill-usage.js +117 -17
- package/dist/agents/skills-status.js +4 -3
- package/dist/agents/skills.js +38 -30
- package/dist/agents/subagent-registry.js +25 -11
- package/dist/agents/system-prompt.js +16 -0
- package/dist/agents/tool-policy.js +19 -3
- package/dist/agents/tools/browser-tool.js +5 -2
- package/dist/agents/tools/image-tool.js +93 -8
- package/dist/agents/tools/sessions-announce-target.js +5 -1
- package/dist/agents/workspace.js +81 -59
- package/dist/auto-reply/command-detection.js +2 -1
- package/dist/auto-reply/reply/directive-handling.js +153 -28
- package/dist/auto-reply/reply/directives.js +17 -2
- package/dist/auto-reply/reply/model-selection.js +8 -3
- package/dist/auto-reply/reply/queue.js +2 -2
- package/dist/auto-reply/reply.js +1 -1
- package/dist/auto-reply/thinking.js +15 -0
- package/dist/browser/chrome.js +1 -1
- package/dist/browser/client.js +2 -0
- package/dist/browser/config.js +6 -2
- package/dist/browser/pw-tools-core.js +3 -0
- package/dist/browser/routes/agent.js +14 -0
- package/dist/canvas-host/server.js +1 -1
- package/dist/capabilities/detector.js +46 -15
- package/dist/capabilities/registry.js +2 -1
- package/dist/cli/cloud-cli.js +70 -7
- package/dist/cli/credential-cli.js +214 -23
- package/dist/cli/gateway-cli.js +1 -1
- package/dist/cli/log-cli.js +25 -0
- package/dist/cli/pairing-cli.js +1 -1
- package/dist/cli/program.js +82 -8
- package/dist/cli/run-main.js +1 -1
- package/dist/cli/skills-cli.js +165 -30
- package/dist/cli/skills-hub-cli.js +68 -36
- package/dist/cli/tool-connector-cli.js +99 -24
- package/dist/cli/upstream-sync-cli.js +253 -96
- package/dist/cli/usage-cli.js +14 -0
- package/dist/commands/auth-choice-options.js +6 -1
- package/dist/commands/auth-choice.js +157 -5
- package/dist/commands/bootstrap-preset.js +26 -12
- package/dist/commands/capabilities.js +33 -6
- package/dist/commands/claude-md.js +3 -2
- package/dist/commands/config-view.js +1 -1
- package/dist/commands/config.js +85 -0
- package/dist/commands/configure.js +4 -4
- package/dist/commands/credential.js +497 -36
- package/dist/commands/cursor-hooks.js +240 -0
- package/dist/commands/cursor-rules.js +14 -188
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/identity.js +29 -32
- package/dist/commands/init.js +304 -20
- package/dist/commands/log.js +134 -0
- package/dist/commands/models/fallbacks.js +1 -1
- package/dist/commands/models/image-fallbacks.js +1 -1
- package/dist/commands/models/list.js +1 -1
- package/dist/commands/models/scan.js +1 -1
- package/dist/commands/onboard-auth.js +27 -2
- package/dist/commands/onboard-eve-identity.js +8 -9
- package/dist/commands/onboard-non-interactive.js +4 -2
- package/dist/commands/onboard-quickstart.js +18 -11
- package/dist/commands/quest-state.js +271 -0
- package/dist/commands/quest.js +53 -13
- package/dist/commands/reset.js +1 -1
- package/dist/commands/sessions-ingest.js +5 -4
- package/dist/commands/setup.js +4 -2
- package/dist/commands/skills-manifest.js +89 -29
- package/dist/commands/status.js +193 -73
- package/dist/commands/suggestions.js +1 -1
- package/dist/commands/usage-tracking.js +32 -0
- package/dist/commands/usage-upload.js +6 -1
- package/dist/config/defaults.js +1 -3
- package/dist/config/includes.js +5 -7
- package/dist/config/io.js +88 -16
- package/dist/config/legacy.js +4 -2
- package/dist/config/paths.js +16 -0
- package/dist/config/sessions.js +9 -5
- package/dist/config/zod-schema.js +4 -3
- package/dist/control-plane/broker/broker.js +131 -78
- package/dist/control-plane/compaction.js +3 -5
- package/dist/control-plane/factory.js +2 -2
- package/dist/control-plane/index.js +2 -2
- package/dist/control-plane/odu/agents.js +28 -23
- package/dist/control-plane/odu/interaction-tools.js +62 -50
- package/dist/control-plane/odu/prompt-loader.js +8 -8
- package/dist/control-plane/odu/runtime.js +87 -75
- package/dist/control-plane/odu-control-plane.js +14 -12
- package/dist/control-plane/single-agent.js +13 -13
- package/dist/credentials/store.js +133 -7
- package/dist/daemon/launchd.js +14 -0
- package/dist/entry.js +0 -0
- package/dist/gateway/server-browser.js +5 -4
- package/dist/gateway/server-methods/cron.js +11 -1
- package/dist/gateway/server.js +14 -7
- package/dist/infra/bonjour.js +1 -1
- package/dist/infra/event-log.js +8 -2
- package/dist/infra/path-env.js +1 -2
- package/dist/infra/provider-usage.auth.js +5 -3
- package/dist/infra/provider-usage.fetch.claude.js +16 -6
- package/dist/infra/provider-usage.fetch.minimax.js +8 -3
- package/dist/infra/provider-usage.js +9 -5
- package/dist/infra/restart.js +2 -2
- package/dist/infra/usage-settings.js +78 -0
- package/dist/infra/usage-suggestions.js +17 -5
- package/dist/infra/usage-upload.js +38 -1
- package/dist/infra/voicewake.js +2 -2
- package/dist/media/image-ops.js +3 -1
- package/dist/memory/index.js +2 -381
- package/dist/native/nexus-cloud/darwin-arm64/nexus-cloud +0 -0
- package/dist/native/nexus-cloud/darwin-arm64/nexus-cloud-rs +0 -0
- package/dist/pairing/pairing-store.js +24 -0
- package/dist/providers/github-copilot-auth.js +1 -1
- package/dist/routing/resolve-route.js +6 -6
- package/dist/routing/session-key.js +3 -1
- package/dist/sessions/send-policy.js +5 -5
- package/dist/slack/monitor.js +22 -1
- package/dist/telegram/reaction-level.js +2 -1
- package/dist/utils.js +8 -3
- package/dist/wizard/onboarding.js +29 -7
- package/docs/AGENTS.default.md +1 -1
- package/docs/configuration.md +1 -1
- package/docs/feature-inventory/overview.md +2 -2
- package/docs/reference/templates/AGENTS.md +172 -109
- package/docs/templates/AGENTS.md +140 -199
- package/docs/templates/BOOTSTRAP.md +40 -20
- package/docs/templates/IDENTITY.md +6 -0
- package/docs/templates/USER.md +22 -2
- package/package.json +3 -1
- package/skills/{notion → connectors/notion}/SKILL.md +1 -1
- package/skills/{filesystem → guides/filesystem}/SKILL.md +1 -1
- package/skills/{onboarding → guides/onboarding}/SKILL.md +1 -1
- package/skills/{onboarding → guides/onboarding}/docs/CAPABILITY_TAXONOMY.md +5 -5
- package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR.md +8 -8
- package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_ONBOARDING.md +2 -2
- package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_SKILLS.md +26 -20
- package/skills/{onboarding → guides/onboarding}/docs/GOAL_STATE_ARCHITECTURE.md +38 -43
- package/skills/{onboarding → guides/onboarding}/docs/NEXUS_SYSTEM_OVERVIEW.md +4 -4
- package/skills/{onboarding → guides/onboarding}/docs/SKILLS_HUB_SPEC.md +1 -1
- package/skills/{onboarding → guides/onboarding}/docs/SKILLS_SPECIFICATION.md +8 -7
- package/skills/{onboarding → guides/onboarding}/docs/SKILL_GATEWAY_DESIGN.md +16 -16
- package/skills/{onboarding → guides/onboarding}/docs/SKILL_GATEWAY_PRD.md +10 -12
- package/skills/guides/onboarding/docs/canonical/00_CONFLICT_ANALYSIS.md +463 -0
- package/skills/guides/onboarding/docs/canonical/01_NEXUS_OVERVIEW.md +167 -0
- package/skills/guides/onboarding/docs/canonical/02_CLI_REFERENCE.md +404 -0
- package/skills/guides/onboarding/docs/canonical/03_STATE_ARCHITECTURE.md +357 -0
- package/skills/guides/onboarding/docs/canonical/04_SKILL_SPECIFICATION.md +393 -0
- package/skills/guides/onboarding/docs/canonical/05_CAPABILITY_TAXONOMY.md +298 -0
- package/skills/guides/onboarding/docs/canonical/06_CAPABILITIES_REFERENCE.md +207 -0
- package/skills/guides/onboarding/docs/canonical/07_AGENT_BINDINGS.md +85 -0
- package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/nexus-cloud.md +2 -2
- package/skills/{onboarding → guides/onboarding}/scripts/ralph/progress.txt +1 -1
- package/skills/{nexus-cloud → tools/nexus-cloud}/SKILL.md +2 -1
- package/skills/{nexus-cloud → tools/nexus-cloud}/docs/setup.md +1 -1
- package/docs/templates/PROFILE.md +0 -14
- /package/skills/{brave-search → connectors/brave-search}/SKILL.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/docs/setup.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/docs/troubleshooting.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/docs/usage.md +0 -0
- /package/skills/{brave-search → connectors/brave-search}/scripts/content.mjs +0 -0
- /package/skills/{brave-search → connectors/brave-search}/scripts/search.mjs +0 -0
- /package/skills/{discord → connectors/discord}/SKILL.md +0 -0
- /package/skills/{gemini → connectors/gemini}/SKILL.md +0 -0
- /package/skills/{github → connectors/github}/SKILL.md +0 -0
- /package/skills/{github → connectors/github}/docs/setup.md +0 -0
- /package/skills/{github → connectors/github}/docs/troubleshooting.md +0 -0
- /package/skills/{google-oauth → connectors/google-oauth}/SKILL.md +0 -0
- /package/skills/{slack → connectors/slack}/SKILL.md +0 -0
- /package/skills/{telegram → connectors/telegram}/SKILL.md +0 -0
- /package/skills/{telegram → connectors/telegram}/docs/pairing.md +0 -0
- /package/skills/{telegram → connectors/telegram}/docs/setup.md +0 -0
- /package/skills/{telegram → connectors/telegram}/docs/webhook.md +0 -0
- /package/skills/{wacli → connectors/wacli}/SKILL.md +0 -0
- /package/skills/{wacli → connectors/wacli}/docs/auth.md +0 -0
- /package/skills/{wacli → connectors/wacli}/docs/backup.md +0 -0
- /package/skills/{wacli → connectors/wacli}/docs/troubleshooting.md +0 -0
- /package/skills/{browser-use-agent-sdk → guides/browser-use-agent-sdk}/SKILL.md +0 -0
- /package/skills/{json-render → guides/json-render}/SKILL.md +0 -0
- /package/skills/{json-render → guides/json-render}/assets/components/README.md +0 -0
- /package/skills/{json-render → guides/json-render}/assets/components/catalog.ts +0 -0
- /package/skills/{json-render → guides/json-render}/assets/components/registry.tsx +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/App.css +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/App.tsx +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/README.md +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/catalog.ts +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/data/nexus-core.json +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/index.css +0 -0
- /package/skills/{json-render → guides/json-render}/assets/demo/registry.tsx +0 -0
- /package/skills/{json-render → guides/json-render}/docs/nexus-state-demo.md +0 -0
- /package/skills/{json-render → guides/json-render}/docs/shadcn-preset.md +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/create-vite-demo.sh +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/README.md +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/catalog.ts +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/package-lock.json +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/package.json +0 -0
- /package/skills/{json-render → guides/json-render}/scripts/llm-server/server.ts +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/CAPABILITIES.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/CLI_GRAMMAR_CREDENTIALS.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/DOCUMENTATION_OVERVIEW.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/ENTITY_MODEL.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/SKILL_INVENTORY.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/STATE_ARCHITECTURE.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/TROUBLESHOOTING.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/USER_JOURNEY.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/WOW_MOMENTS.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/agent-apple-id.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/1password.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/TEMPLATE.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/aix.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/bird.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/brave-search.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/comms.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/computer-use.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/cron-and-heartbeat.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/eve.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/github.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/gog.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/homebrew-prereqs.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/qmd.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/telegram.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/wacli.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/docs/skill-deep-dives/weather.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/prd.json +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/prompt.md +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/ralph.log +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/ralph/ralph.sh +0 -0
- /package/skills/{onboarding → guides/onboarding}/scripts/setup-cursor-skills.sh +0 -0
- /package/skills/{1password → tools/1password}/SKILL.md +0 -0
- /package/skills/{1password → tools/1password}/docs/setup.md +0 -0
- /package/skills/{1password → tools/1password}/docs/troubleshooting.md +0 -0
- /package/skills/{1password → tools/1password}/references/cli-examples.md +0 -0
- /package/skills/{1password → tools/1password}/references/get-started.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/SKILL.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/docs/browser-use-eval.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/docs/first-tests.md +0 -0
- /package/skills/{agent-browser → tools/agent-browser}/docs/wordle-nyt-eval.js +0 -0
- /package/skills/{aix → tools/aix}/SKILL.md +0 -0
- /package/skills/{aix → tools/aix}/docs/embeddings.md +0 -0
- /package/skills/{aix → tools/aix}/docs/setup.md +0 -0
- /package/skills/{aix → tools/aix}/docs/troubleshooting.md +0 -0
- /package/skills/{aix → tools/aix}/references/sql.md +0 -0
- /package/skills/{apple-notes → tools/apple-notes}/SKILL.md +0 -0
- /package/skills/{apple-reminders → tools/apple-reminders}/SKILL.md +0 -0
- /package/skills/{bear-notes → tools/bear-notes}/SKILL.md +0 -0
- /package/skills/{bird → tools/bird}/SKILL.md +0 -0
- /package/skills/{bird → tools/bird}/docs/auth.md +0 -0
- /package/skills/{bird → tools/bird}/docs/troubleshooting.md +0 -0
- /package/skills/{blogwatcher → tools/blogwatcher}/SKILL.md +0 -0
- /package/skills/{blucli → tools/blucli}/SKILL.md +0 -0
- /package/skills/{camsnap → tools/camsnap}/SKILL.md +0 -0
- /package/skills/{clawdhub → tools/clawdhub}/SKILL.md +0 -0
- /package/skills/{coding-agent → tools/coding-agent}/SKILL.md +0 -0
- /package/skills/{comms → tools/comms}/SKILL.md +0 -0
- /package/skills/{comms → tools/comms}/docs/adapters.md +0 -0
- /package/skills/{comms → tools/comms}/docs/setup.md +0 -0
- /package/skills/{comms → tools/comms}/docs/troubleshooting.md +0 -0
- /package/skills/{comms → tools/comms}/references/schema.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/SKILL.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/open-interpreter.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/peekaboo.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/setup.md +0 -0
- /package/skills/{computer-use → tools/computer-use}/docs/troubleshooting.md +0 -0
- /package/skills/{eightctl → tools/eightctl}/SKILL.md +0 -0
- /package/skills/{eve → tools/eve}/SKILL.md +0 -0
- /package/skills/{eve → tools/eve}/docs/dual-account.md +0 -0
- /package/skills/{eve → tools/eve}/docs/intelligence.md +0 -0
- /package/skills/{eve → tools/eve}/docs/setup.md +0 -0
- /package/skills/{eve → tools/eve}/docs/troubleshooting.md +0 -0
- /package/skills/{eve → tools/eve}/scripts/setup-dual-account.sh +0 -0
- /package/skills/{food-order → tools/food-order}/SKILL.md +0 -0
- /package/skills/{gh → tools/gh}/SKILL.md +0 -0
- /package/skills/{gh → tools/gh}/docs/usage.md +0 -0
- /package/skills/{gifgrep → tools/gifgrep}/SKILL.md +0 -0
- /package/skills/{gog → tools/gog}/SKILL.md +0 -0
- /package/skills/{gog → tools/gog}/docs/portability.md +0 -0
- /package/skills/{gog → tools/gog}/docs/setup.md +0 -0
- /package/skills/{gog → tools/gog}/docs/troubleshooting.md +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/README.md +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/add_test_users.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/auth_add_accounts.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/auth_add_accounts_manual.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/create_oauth_client.py +0 -0
- /package/skills/{gog → tools/gog}/scripts/cdp/launch_cdp_chrome.sh +0 -0
- /package/skills/{goplaces → tools/goplaces}/SKILL.md +0 -0
- /package/skills/{imsg → tools/imsg}/SKILL.md +0 -0
- /package/skills/{local-places → tools/local-places}/SERVER_README.md +0 -0
- /package/skills/{local-places → tools/local-places}/SKILL.md +0 -0
- /package/skills/{local-places → tools/local-places}/pyproject.toml +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__init__.py +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/__init__.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/google_places.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/main.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/__pycache__/schemas.cpython-314.pyc +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/google_places.py +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/main.py +0 -0
- /package/skills/{local-places → tools/local-places}/src/local_places/schemas.py +0 -0
- /package/skills/{mcporter → tools/mcporter}/SKILL.md +0 -0
- /package/skills/{model-usage → tools/model-usage}/SKILL.md +0 -0
- /package/skills/{model-usage → tools/model-usage}/references/codexbar-cli.md +0 -0
- /package/skills/{model-usage → tools/model-usage}/scripts/model_usage.py +0 -0
- /package/skills/{nano-banana-pro → tools/nano-banana-pro}/SKILL.md +0 -0
- /package/skills/{nano-banana-pro → tools/nano-banana-pro}/scripts/generate_image.py +0 -0
- /package/skills/{nano-pdf → tools/nano-pdf}/SKILL.md +0 -0
- /package/skills/{nexus-cloud → tools/nexus-cloud}/docs/security.md +0 -0
- /package/skills/{nexus-cloud → tools/nexus-cloud}/docs/troubleshooting.md +0 -0
- /package/skills/{obsidian → tools/obsidian}/SKILL.md +0 -0
- /package/skills/{openai-image-gen → tools/openai-image-gen}/SKILL.md +0 -0
- /package/skills/{openai-image-gen → tools/openai-image-gen}/scripts/gen.py +0 -0
- /package/skills/{openai-whisper → tools/openai-whisper}/SKILL.md +0 -0
- /package/skills/{openai-whisper-api → tools/openai-whisper-api}/SKILL.md +0 -0
- /package/skills/{openai-whisper-api → tools/openai-whisper-api}/scripts/transcribe.sh +0 -0
- /package/skills/{openhue → tools/openhue}/SKILL.md +0 -0
- /package/skills/{oracle → tools/oracle}/SKILL.md +0 -0
- /package/skills/{ordercli → tools/ordercli}/SKILL.md +0 -0
- /package/skills/{peekaboo → tools/peekaboo}/SKILL.md +0 -0
- /package/skills/{qmd → tools/qmd}/SKILL.md +0 -0
- /package/skills/{qmd → tools/qmd}/docs/mcp.md +0 -0
- /package/skills/{qmd → tools/qmd}/docs/ollama.md +0 -0
- /package/skills/{qmd → tools/qmd}/docs/setup.md +0 -0
- /package/skills/{sag → tools/sag}/SKILL.md +0 -0
- /package/skills/{skill-cli-template → tools/skill-cli-template}/SKILL.md +0 -0
- /package/skills/{songsee → tools/songsee}/SKILL.md +0 -0
- /package/skills/{sonoscli → tools/sonoscli}/SKILL.md +0 -0
- /package/skills/{spotify-player → tools/spotify-player}/SKILL.md +0 -0
- /package/skills/{summarize → tools/summarize}/SKILL.md +0 -0
- /package/skills/{things-mac → tools/things-mac}/SKILL.md +0 -0
- /package/skills/{tmux → tools/tmux}/SKILL.md +0 -0
- /package/skills/{tmux → tools/tmux}/scripts/find-sessions.sh +0 -0
- /package/skills/{tmux → tools/tmux}/scripts/wait-for-text.sh +0 -0
- /package/skills/{trello → tools/trello}/SKILL.md +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/SKILL.md +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/auto-port.sh +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-all.sh +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-nexus.sh +0 -0
- /package/skills/{upstream-sync → tools/upstream-sync}/scripts/check-pi-ai.sh +0 -0
- /package/skills/{video-frames → tools/video-frames}/SKILL.md +0 -0
- /package/skills/{video-frames → tools/video-frames}/scripts/frame.sh +0 -0
- /package/skills/{weather → tools/weather}/SKILL.md +0 -0
- /package/skills/{weather → tools/weather}/docs/usage.md +0 -0
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* bundle-dispatch-oldest - Dispatch oldest pending bundles
|
|
22
22
|
*/
|
|
23
23
|
import { execSync, spawn } from "node:child_process";
|
|
24
|
-
import {
|
|
24
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, writeFileSync, } from "node:fs";
|
|
25
25
|
import { dirname, join, resolve } from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
27
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -29,6 +29,14 @@ const PROJECT_ROOT = resolve(__dirname, "../..");
|
|
|
29
29
|
const STATE_FILE = join(PROJECT_ROOT, ".upstream-sync/state.json");
|
|
30
30
|
const TMUX_SOCKET = "/tmp/upstream-sync.sock";
|
|
31
31
|
const RUNS_DIR = join(PROJECT_ROOT, ".upstream-sync/runs");
|
|
32
|
+
const DEFAULT_MAX_CONCURRENT = 6;
|
|
33
|
+
function getMaxConcurrent() {
|
|
34
|
+
const raw = process.env.NEXUS_UPSTREAM_SYNC_MAX_CONCURRENT;
|
|
35
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
36
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
37
|
+
return parsed;
|
|
38
|
+
return DEFAULT_MAX_CONCURRENT;
|
|
39
|
+
}
|
|
32
40
|
// ============================================================================
|
|
33
41
|
// State Management
|
|
34
42
|
// ============================================================================
|
|
@@ -120,7 +128,11 @@ function getTypePriority(type, breaking) {
|
|
|
120
128
|
function bundleDirectCommits(state) {
|
|
121
129
|
const directCommits = Object.entries(state.merges)
|
|
122
130
|
.filter(([_, e]) => e.isDirectCommit && e.status === "new" && !e.bundleId)
|
|
123
|
-
.map(([sha, e]) => ({
|
|
131
|
+
.map(([sha, e]) => ({
|
|
132
|
+
sha,
|
|
133
|
+
entry: e,
|
|
134
|
+
parsed: parseConventionalCommit(e.title),
|
|
135
|
+
}));
|
|
124
136
|
if (directCommits.length === 0)
|
|
125
137
|
return [];
|
|
126
138
|
// Group by: date (day) + scope + type
|
|
@@ -135,7 +147,7 @@ function bundleDirectCommits(state) {
|
|
|
135
147
|
if (!groups.has(key)) {
|
|
136
148
|
groups.set(key, []);
|
|
137
149
|
}
|
|
138
|
-
groups.get(key)
|
|
150
|
+
groups.get(key)?.push(commit);
|
|
139
151
|
}
|
|
140
152
|
// Convert groups to bundles
|
|
141
153
|
const bundles = [];
|
|
@@ -177,8 +189,17 @@ function bundleDirectCommits(state) {
|
|
|
177
189
|
return bundles.sort((a, b) => b.priority - a.priority);
|
|
178
190
|
}
|
|
179
191
|
// Types that should ALWAYS be consolidated into daily bundles (low priority, safe to batch)
|
|
180
|
-
const LOW_PRIORITY_TYPES = new Set([
|
|
181
|
-
|
|
192
|
+
const LOW_PRIORITY_TYPES = new Set([
|
|
193
|
+
"fix",
|
|
194
|
+
"docs",
|
|
195
|
+
"test",
|
|
196
|
+
"chore",
|
|
197
|
+
"style",
|
|
198
|
+
"ci",
|
|
199
|
+
"build",
|
|
200
|
+
]);
|
|
201
|
+
const BUNDLE_STRATEGY = (process.env.NEXUS_UPSTREAM_SYNC_BUNDLE_STRATEGY ??
|
|
202
|
+
"aggressive");
|
|
182
203
|
const MIN_BUNDLE_LINES = (() => {
|
|
183
204
|
const raw = process.env.NEXUS_UPSTREAM_SYNC_BUNDLE_MIN_LINES ?? "1000";
|
|
184
205
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -314,7 +335,7 @@ function consolidateBundles(state, bundles) {
|
|
|
314
335
|
if (!scopeBundles.has(key)) {
|
|
315
336
|
scopeBundles.set(key, []);
|
|
316
337
|
}
|
|
317
|
-
scopeBundles.get(key)
|
|
338
|
+
scopeBundles.get(key)?.push(bundle);
|
|
318
339
|
continue;
|
|
319
340
|
}
|
|
320
341
|
if (bundle.type && LOW_PRIORITY_TYPES.has(bundle.type)) {
|
|
@@ -351,7 +372,7 @@ function consolidateBundles(state, bundles) {
|
|
|
351
372
|
if (!byDate.has(date)) {
|
|
352
373
|
byDate.set(date, []);
|
|
353
374
|
}
|
|
354
|
-
byDate.get(date)
|
|
375
|
+
byDate.get(date)?.push(bundle);
|
|
355
376
|
}
|
|
356
377
|
for (const [date, group] of byDate) {
|
|
357
378
|
if (group.length === 1) {
|
|
@@ -384,7 +405,7 @@ function consolidateBundles(state, bundles) {
|
|
|
384
405
|
if (!byDateType.has(key)) {
|
|
385
406
|
byDateType.set(key, []);
|
|
386
407
|
}
|
|
387
|
-
byDateType.get(key)
|
|
408
|
+
byDateType.get(key)?.push(bundle);
|
|
388
409
|
}
|
|
389
410
|
for (const [key, group] of byDateType) {
|
|
390
411
|
if (group.length === 1) {
|
|
@@ -418,7 +439,7 @@ function printBundles(bundles) {
|
|
|
418
439
|
console.log("No bundles to display.");
|
|
419
440
|
return;
|
|
420
441
|
}
|
|
421
|
-
console.log(
|
|
442
|
+
console.log(`\n${"═".repeat(60)}`);
|
|
422
443
|
console.log(" DIRECT COMMIT BUNDLES");
|
|
423
444
|
console.log("═".repeat(60));
|
|
424
445
|
// Group by priority tier
|
|
@@ -442,13 +463,15 @@ function printBundles(bundles) {
|
|
|
442
463
|
printTier("FIXES", "🐛", fixes);
|
|
443
464
|
printTier("REFACTORS", "♻️", refactors);
|
|
444
465
|
printTier("OTHER", "📦", other);
|
|
445
|
-
console.log(
|
|
466
|
+
console.log(`\n${"═".repeat(60)}`);
|
|
446
467
|
console.log(` Total: ${bundles.length} bundles, ${bundles.reduce((sum, b) => sum + b.commits.length, 0)} commits`);
|
|
447
|
-
console.log("═".repeat(60)
|
|
468
|
+
console.log(`${"═".repeat(60)}\n`);
|
|
448
469
|
}
|
|
449
470
|
function categorizeSplitBucket(title, type) {
|
|
450
471
|
const lower = title.toLowerCase();
|
|
451
|
-
if (lower.includes("appcast") ||
|
|
472
|
+
if (lower.includes("appcast") ||
|
|
473
|
+
lower.includes("release") ||
|
|
474
|
+
lower.includes("changelog")) {
|
|
452
475
|
return "release";
|
|
453
476
|
}
|
|
454
477
|
if (type === "docs" ||
|
|
@@ -483,7 +506,9 @@ function categorizeSplitBucket(title, type) {
|
|
|
483
506
|
if (lower.includes("cron") || lower.includes("gateway")) {
|
|
484
507
|
return "gateway";
|
|
485
508
|
}
|
|
486
|
-
if (lower.includes("voice") ||
|
|
509
|
+
if (lower.includes("voice") ||
|
|
510
|
+
lower.includes("plugin") ||
|
|
511
|
+
lower.includes("apply_patch")) {
|
|
487
512
|
return "voice";
|
|
488
513
|
}
|
|
489
514
|
if (type && type !== "other")
|
|
@@ -495,7 +520,11 @@ function categorizeSplitBucket(title, type) {
|
|
|
495
520
|
// ============================================================================
|
|
496
521
|
function exec(cmd, cwd) {
|
|
497
522
|
try {
|
|
498
|
-
return execSync(cmd, {
|
|
523
|
+
return execSync(cmd, {
|
|
524
|
+
cwd,
|
|
525
|
+
encoding: "utf-8",
|
|
526
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
527
|
+
}).trim();
|
|
499
528
|
}
|
|
500
529
|
catch (e) {
|
|
501
530
|
const error = e;
|
|
@@ -503,7 +532,8 @@ function exec(cmd, cwd) {
|
|
|
503
532
|
}
|
|
504
533
|
}
|
|
505
534
|
function isDispatchDisabled(state) {
|
|
506
|
-
return process.env.NEXUS_UPSTREAM_SYNC_DISABLE_DISPATCH === "1" ||
|
|
535
|
+
return (process.env.NEXUS_UPSTREAM_SYNC_DISABLE_DISPATCH === "1" ||
|
|
536
|
+
Boolean(state?.dispatchDisabled));
|
|
507
537
|
}
|
|
508
538
|
function assertDispatchEnabled(state) {
|
|
509
539
|
if (isDispatchDisabled(state)) {
|
|
@@ -524,7 +554,10 @@ function branchExists(branchName) {
|
|
|
524
554
|
}
|
|
525
555
|
function isGitAncestor(ancestor, target, cwd) {
|
|
526
556
|
try {
|
|
527
|
-
execSync(`git merge-base --is-ancestor ${ancestor} ${target}`, {
|
|
557
|
+
execSync(`git merge-base --is-ancestor ${ancestor} ${target}`, {
|
|
558
|
+
cwd,
|
|
559
|
+
stdio: "ignore",
|
|
560
|
+
});
|
|
528
561
|
return true;
|
|
529
562
|
}
|
|
530
563
|
catch {
|
|
@@ -539,7 +572,8 @@ function assertBranchBasedOnMain(branchName, worktreePath) {
|
|
|
539
572
|
}
|
|
540
573
|
return mainCommit;
|
|
541
574
|
}
|
|
542
|
-
if (branchExists(branchName) &&
|
|
575
|
+
if (branchExists(branchName) &&
|
|
576
|
+
!isGitAncestor(mainCommit, branchName, PROJECT_ROOT)) {
|
|
543
577
|
throw new Error(`Branch ${branchName} is not based on current main (${mainCommit}). Delete/recreate before dispatch.`);
|
|
544
578
|
}
|
|
545
579
|
return mainCommit;
|
|
@@ -559,7 +593,7 @@ function getRunPaths(runId) {
|
|
|
559
593
|
}
|
|
560
594
|
function bashSingleQuote(s) {
|
|
561
595
|
// 'foo'"'"'bar' pattern
|
|
562
|
-
return `'${s.replace(/'/g, `'
|
|
596
|
+
return `'${s.replace(/'/g, `'"'"'`)}'`;
|
|
563
597
|
}
|
|
564
598
|
function isPidRunning(pid) {
|
|
565
599
|
try {
|
|
@@ -580,6 +614,19 @@ function readLastLines(filePath, maxLines) {
|
|
|
580
614
|
return "";
|
|
581
615
|
}
|
|
582
616
|
}
|
|
617
|
+
function hasWorktreeCommitBeyondMain(worktreePath) {
|
|
618
|
+
try {
|
|
619
|
+
const log = execSync(`git log --oneline main..HEAD`, {
|
|
620
|
+
cwd: worktreePath,
|
|
621
|
+
encoding: "utf-8",
|
|
622
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
623
|
+
}).trim();
|
|
624
|
+
return Boolean(log);
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
583
630
|
function isRunStalled(runLog, runStartedAt) {
|
|
584
631
|
if (!runLog || !runStartedAt)
|
|
585
632
|
return false;
|
|
@@ -605,13 +652,19 @@ function startDetachedAgentRun(opts) {
|
|
|
605
652
|
const promptArg = bashSingleQuote(opts.prompt);
|
|
606
653
|
const commitMsgArg = bashSingleQuote(opts.commitMessage);
|
|
607
654
|
const exitPathArg = bashSingleQuote(exitPath);
|
|
655
|
+
const nodeMajor = Number((process.versions.node ?? "").split(".")[0] ?? 0);
|
|
656
|
+
const skipInstall = process.env.NEXUS_UPSTREAM_SYNC_SKIP_INSTALL === "1" ||
|
|
657
|
+
(nodeMajor > 0 && nodeMajor < 20);
|
|
658
|
+
const installCmd = skipInstall
|
|
659
|
+
? `echo "SKIP_NPM_INSTALL"; `
|
|
660
|
+
: `npm install || echo "WARN: npm install failed; continuing"; `;
|
|
608
661
|
const cmd = `rc=0; ` +
|
|
609
662
|
`echo "== upstream-sync run ${opts.runId} =="; ` +
|
|
610
663
|
`echo "startedAt=${startedAt}"; ` +
|
|
611
664
|
`echo "cwd=${opts.cwd}"; ` +
|
|
612
665
|
`echo ""; ` +
|
|
613
|
-
|
|
614
|
-
`
|
|
666
|
+
installCmd +
|
|
667
|
+
`codex exec --dangerously-bypass-approvals-and-sandbox ${promptArg} || rc=$?; ` +
|
|
615
668
|
`if [ $rc -eq 0 ]; then ` +
|
|
616
669
|
` git add -A || rc=$?; ` +
|
|
617
670
|
` git reset PORT_TASK.md package-lock.json 2>/dev/null || true; ` +
|
|
@@ -631,14 +684,25 @@ function startDetachedAgentRun(opts) {
|
|
|
631
684
|
env: process.env,
|
|
632
685
|
});
|
|
633
686
|
try {
|
|
634
|
-
writeFileSync(metaPath, JSON.stringify({
|
|
687
|
+
writeFileSync(metaPath, `${JSON.stringify({
|
|
688
|
+
runId: opts.runId,
|
|
689
|
+
pid: child.pid,
|
|
690
|
+
startedAt,
|
|
691
|
+
cwd: opts.cwd,
|
|
692
|
+
logPath,
|
|
693
|
+
exitPath,
|
|
694
|
+
}, null, 2)}\n`);
|
|
635
695
|
}
|
|
636
696
|
catch {
|
|
637
697
|
// ignore
|
|
638
698
|
}
|
|
639
699
|
child.unref();
|
|
640
700
|
closeSync(fd);
|
|
641
|
-
|
|
701
|
+
const pid = child.pid;
|
|
702
|
+
if (!pid) {
|
|
703
|
+
throw new Error("Expected child process pid");
|
|
704
|
+
}
|
|
705
|
+
return { pid, logPath, exitPath, metaPath, startedAt };
|
|
642
706
|
}
|
|
643
707
|
function getUpstreamPath(state) {
|
|
644
708
|
return resolve(PROJECT_ROOT, state.upstream.path);
|
|
@@ -682,7 +746,14 @@ function findNewCommits(state) {
|
|
|
682
746
|
const prMatch = subject.match(/Merge pull request #(\d+) from (.+)/);
|
|
683
747
|
const prNumber = prMatch ? parseInt(prMatch[1], 10) : undefined;
|
|
684
748
|
const title = prMatch ? prMatch[2] : subject;
|
|
685
|
-
commits.push({
|
|
749
|
+
commits.push({
|
|
750
|
+
sha,
|
|
751
|
+
prNumber,
|
|
752
|
+
title,
|
|
753
|
+
date,
|
|
754
|
+
isDirectCommit: false,
|
|
755
|
+
author,
|
|
756
|
+
});
|
|
686
757
|
}
|
|
687
758
|
else {
|
|
688
759
|
// Check if this references a PR anywhere in the subject:
|
|
@@ -698,18 +769,31 @@ function findNewCommits(state) {
|
|
|
698
769
|
.replace(/\s*\(#\d+\)\s*/g, " ")
|
|
699
770
|
.replace(/\s+/g, " ")
|
|
700
771
|
.trim();
|
|
701
|
-
commits.push({
|
|
772
|
+
commits.push({
|
|
773
|
+
sha,
|
|
774
|
+
prNumber,
|
|
775
|
+
title,
|
|
776
|
+
date,
|
|
777
|
+
isDirectCommit: false,
|
|
778
|
+
author,
|
|
779
|
+
});
|
|
702
780
|
}
|
|
703
781
|
else {
|
|
704
782
|
// True direct commit - no PR number reference
|
|
705
|
-
commits.push({
|
|
783
|
+
commits.push({
|
|
784
|
+
sha,
|
|
785
|
+
title: subject,
|
|
786
|
+
date,
|
|
787
|
+
isDirectCommit: true,
|
|
788
|
+
author,
|
|
789
|
+
});
|
|
706
790
|
}
|
|
707
791
|
}
|
|
708
792
|
}
|
|
709
793
|
return commits;
|
|
710
794
|
}
|
|
711
795
|
// Legacy alias for compatibility
|
|
712
|
-
function
|
|
796
|
+
function _findNewMerges(state) {
|
|
713
797
|
return findNewCommits(state);
|
|
714
798
|
}
|
|
715
799
|
// ============================================================================
|
|
@@ -720,7 +804,9 @@ function generatePortTask(state, sha, entry) {
|
|
|
720
804
|
const baseCommit = entry.baseCommit || exec("git rev-parse main", PROJECT_ROOT);
|
|
721
805
|
// Get the diff for this commit
|
|
722
806
|
const diff = exec(`git show ${sha} --stat`, upstreamPath);
|
|
723
|
-
const commitType = entry.isDirectCommit
|
|
807
|
+
const commitType = entry.isDirectCommit
|
|
808
|
+
? "Direct Commit"
|
|
809
|
+
: `PR #${entry.prNumber || "unknown"}`;
|
|
724
810
|
const commitLabel = entry.isDirectCommit
|
|
725
811
|
? `⭐ DIRECT COMMIT (HIGH PRIORITY)`
|
|
726
812
|
: `PR #${entry.prNumber}`;
|
|
@@ -792,7 +878,7 @@ function dispatchAgent(state, sha) {
|
|
|
792
878
|
exec(`git worktree add ${worktreePath} ${branchName}`, PROJECT_ROOT);
|
|
793
879
|
}
|
|
794
880
|
catch {
|
|
795
|
-
throw new Error(`Failed to create worktree: ${e}`);
|
|
881
|
+
throw new Error(`Failed to create worktree: ${String(e)}`);
|
|
796
882
|
}
|
|
797
883
|
}
|
|
798
884
|
}
|
|
@@ -804,7 +890,12 @@ function dispatchAgent(state, sha) {
|
|
|
804
890
|
const runId = `pr-${identifier}-${Date.now()}`;
|
|
805
891
|
const prompt = "Read PORT_TASK.md and complete the porting task. Commit your changes when done (a supervisor will also attempt to commit).";
|
|
806
892
|
const commitMessage = "feat: port upstream changes";
|
|
807
|
-
const run = startDetachedAgentRun({
|
|
893
|
+
const run = startDetachedAgentRun({
|
|
894
|
+
runId,
|
|
895
|
+
cwd: worktreePath,
|
|
896
|
+
prompt,
|
|
897
|
+
commitMessage,
|
|
898
|
+
});
|
|
808
899
|
console.log(` Started detached runner pid=${run.pid}`);
|
|
809
900
|
// Update state
|
|
810
901
|
entry.status = "porting";
|
|
@@ -851,7 +942,8 @@ function checkAgentStatus(state, sha) {
|
|
|
851
942
|
return "idle";
|
|
852
943
|
// If log indicates codex finished and is waiting on commit, treat as idle.
|
|
853
944
|
const tail = entry.runLog ? readLastLines(entry.runLog, 30) : "";
|
|
854
|
-
if (tail.includes("Your task is complete") ||
|
|
945
|
+
if (tail.includes("Your task is complete") ||
|
|
946
|
+
tail.includes("Run these commands NOW"))
|
|
855
947
|
return "idle";
|
|
856
948
|
return "working";
|
|
857
949
|
}
|
|
@@ -884,7 +976,8 @@ function checkAgentStatus(state, sha) {
|
|
|
884
976
|
try {
|
|
885
977
|
const output = exec(`tmux -S "${TMUX_SOCKET}" capture-pane -p -t ${sessionName} -S -10`);
|
|
886
978
|
// Check if codex is waiting for commit instructions
|
|
887
|
-
if (output.includes("Your task is complete") ||
|
|
979
|
+
if (output.includes("Your task is complete") ||
|
|
980
|
+
output.includes("Run these commands NOW")) {
|
|
888
981
|
if (entry.portBranch) {
|
|
889
982
|
try {
|
|
890
983
|
const commits = exec(`git log main..${entry.portBranch} --oneline`, PROJECT_ROOT);
|
|
@@ -958,7 +1051,10 @@ function nudgeIdleAgent(worktreePath, sessionName) {
|
|
|
958
1051
|
}
|
|
959
1052
|
let hasChanges = false;
|
|
960
1053
|
try {
|
|
961
|
-
execSync("git diff --cached --quiet", {
|
|
1054
|
+
execSync("git diff --cached --quiet", {
|
|
1055
|
+
cwd: worktreePath,
|
|
1056
|
+
stdio: "ignore",
|
|
1057
|
+
});
|
|
962
1058
|
}
|
|
963
1059
|
catch {
|
|
964
1060
|
hasChanges = true;
|
|
@@ -1014,7 +1110,9 @@ function updateAllStatuses(state) {
|
|
|
1014
1110
|
if (entry.bundleId)
|
|
1015
1111
|
continue;
|
|
1016
1112
|
if (entry.status === "porting") {
|
|
1017
|
-
const label = entry.prNumber
|
|
1113
|
+
const label = entry.prNumber
|
|
1114
|
+
? `PR #${entry.prNumber}`
|
|
1115
|
+
: `Commit ${sha.slice(0, 8)}`;
|
|
1018
1116
|
const status = checkAgentStatus(state, sha);
|
|
1019
1117
|
if (status === "done") {
|
|
1020
1118
|
// Agent finished and committed - close session and mark complete
|
|
@@ -1098,7 +1196,7 @@ function checkBundleAgentStatus(bundleId) {
|
|
|
1098
1196
|
encoding: "utf-8",
|
|
1099
1197
|
stdio: ["pipe", "pipe", "ignore"],
|
|
1100
1198
|
}).trim();
|
|
1101
|
-
if (subject) {
|
|
1199
|
+
if (subject && hasWorktreeCommitBeyondMain(worktreePath)) {
|
|
1102
1200
|
const files = execSync(`git show --name-only --pretty=format: -1`, {
|
|
1103
1201
|
cwd: worktreePath,
|
|
1104
1202
|
encoding: "utf-8",
|
|
@@ -1123,7 +1221,8 @@ function checkBundleAgentStatus(bundleId) {
|
|
|
1123
1221
|
if (isRunStalled(bundle.runLog, bundle.runStartedAt))
|
|
1124
1222
|
return "idle";
|
|
1125
1223
|
const tail = bundle.runLog ? readLastLines(bundle.runLog, 40) : "";
|
|
1126
|
-
if (tail.includes("Your task is complete") ||
|
|
1224
|
+
if (tail.includes("Your task is complete") ||
|
|
1225
|
+
tail.includes("Run these commands NOW"))
|
|
1127
1226
|
return "idle";
|
|
1128
1227
|
return "working";
|
|
1129
1228
|
}
|
|
@@ -1138,10 +1237,11 @@ function checkBundleAgentStatus(bundleId) {
|
|
|
1138
1237
|
encoding: "utf-8",
|
|
1139
1238
|
stdio: ["pipe", "pipe", "ignore"],
|
|
1140
1239
|
}).trim();
|
|
1141
|
-
if (log.includes("port") ||
|
|
1240
|
+
if ((log.includes("port") ||
|
|
1142
1241
|
log.includes("feat:") ||
|
|
1143
1242
|
log.includes("fix:") ||
|
|
1144
|
-
log.includes("refactor"))
|
|
1243
|
+
log.includes("refactor")) &&
|
|
1244
|
+
hasWorktreeCommitBeyondMain(worktreePath)) {
|
|
1145
1245
|
// Ensure the commit includes actual changes beyond PORT_TASK/package-lock
|
|
1146
1246
|
try {
|
|
1147
1247
|
const files = execSync(`git show --name-only --pretty=format: -1`, {
|
|
@@ -1169,7 +1269,8 @@ function checkBundleAgentStatus(bundleId) {
|
|
|
1169
1269
|
const pane = execSync(`tmux -S ${TMUX_SOCKET} capture-pane -t "${sessionName}" -p 2>/dev/null`, {
|
|
1170
1270
|
encoding: "utf-8",
|
|
1171
1271
|
});
|
|
1172
|
-
if (pane.includes("Your task is complete") ||
|
|
1272
|
+
if (pane.includes("Your task is complete") ||
|
|
1273
|
+
pane.includes("Run these commands NOW")) {
|
|
1173
1274
|
return "idle"; // Done but hasn't exited
|
|
1174
1275
|
}
|
|
1175
1276
|
return "working";
|
|
@@ -1246,7 +1347,14 @@ function formatDuration(seconds) {
|
|
|
1246
1347
|
return `${mins}m`;
|
|
1247
1348
|
}
|
|
1248
1349
|
function printStatus(state) {
|
|
1249
|
-
const counts = {
|
|
1350
|
+
const counts = {
|
|
1351
|
+
new: 0,
|
|
1352
|
+
porting: 0,
|
|
1353
|
+
pending_review: 0,
|
|
1354
|
+
shelved: 0,
|
|
1355
|
+
merged: 0,
|
|
1356
|
+
ignored: 0,
|
|
1357
|
+
};
|
|
1250
1358
|
const byStatus = {
|
|
1251
1359
|
pending_review: [],
|
|
1252
1360
|
shelved: [],
|
|
@@ -1261,7 +1369,7 @@ function printStatus(state) {
|
|
|
1261
1369
|
counts[statusKey]++;
|
|
1262
1370
|
byStatus[statusKey].push({ sha, entry });
|
|
1263
1371
|
}
|
|
1264
|
-
console.log(
|
|
1372
|
+
console.log(`\n${"═".repeat(60)}`);
|
|
1265
1373
|
console.log(" UPSTREAM SYNC STATUS");
|
|
1266
1374
|
console.log("═".repeat(60));
|
|
1267
1375
|
console.log(` Last fetched: ${state.lastFetched}`);
|
|
@@ -1302,7 +1410,9 @@ function printStatus(state) {
|
|
|
1302
1410
|
const sessionName = getBundleSessionName(bundle.id);
|
|
1303
1411
|
const agentStatus = checkBundleAgentStatus(bundle.id);
|
|
1304
1412
|
const session = tmuxSessions.get(sessionName);
|
|
1305
|
-
const runtimeSec = session
|
|
1413
|
+
const runtimeSec = session
|
|
1414
|
+
? Math.floor(Date.now() / 1000) - session.created
|
|
1415
|
+
: 0;
|
|
1306
1416
|
const runtime = formatDuration(runtimeSec);
|
|
1307
1417
|
const statusLabel = agentStatus === "idle"
|
|
1308
1418
|
? "stuck (idle)"
|
|
@@ -1359,7 +1469,9 @@ function printStatus(state) {
|
|
|
1359
1469
|
if (byStatus.porting.length > 0) {
|
|
1360
1470
|
console.log(" ⚙️ IN PROGRESS:");
|
|
1361
1471
|
for (const { entry } of byStatus.porting) {
|
|
1362
|
-
const status = entry.tmuxSession
|
|
1472
|
+
const status = entry.tmuxSession
|
|
1473
|
+
? `tmux: ${entry.tmuxSession}`
|
|
1474
|
+
: "unknown";
|
|
1363
1475
|
console.log(` PR #${entry.prNumber}: ${entry.title} [${status}]`);
|
|
1364
1476
|
}
|
|
1365
1477
|
console.log("");
|
|
@@ -1371,7 +1483,7 @@ function printStatus(state) {
|
|
|
1371
1483
|
return -1;
|
|
1372
1484
|
if (!a.entry.isDirectCommit && b.entry.isDirectCommit)
|
|
1373
1485
|
return 1;
|
|
1374
|
-
return new Date(b.entry.date).getTime() - new Date(a.entry.date).getTime();
|
|
1486
|
+
return (new Date(b.entry.date).getTime() - new Date(a.entry.date).getTime());
|
|
1375
1487
|
});
|
|
1376
1488
|
const directCount = sortedNew.filter((x) => x.entry.isDirectCommit).length;
|
|
1377
1489
|
console.log(` 🆕 NEW (will dispatch agents): ${directCount} direct commits, ${byStatus.new.length - directCount} PRs`);
|
|
@@ -1380,7 +1492,9 @@ function printStatus(state) {
|
|
|
1380
1492
|
console.log(` ⭐ DIRECT: ${entry.title} (${entry.author || "unknown"})`);
|
|
1381
1493
|
}
|
|
1382
1494
|
// Then PRs
|
|
1383
|
-
for (const { entry } of sortedNew
|
|
1495
|
+
for (const { entry } of sortedNew
|
|
1496
|
+
.filter((x) => !x.entry.isDirectCommit)
|
|
1497
|
+
.slice(0, 10)) {
|
|
1384
1498
|
console.log(` PR #${entry.prNumber}: ${entry.title}`);
|
|
1385
1499
|
}
|
|
1386
1500
|
const remainingPRs = sortedNew.filter((x) => !x.entry.isDirectCommit).length - 10;
|
|
@@ -1410,7 +1524,7 @@ function printStatus(state) {
|
|
|
1410
1524
|
console.log(" bundle-dispatch-oldest - Dispatch oldest pending bundles");
|
|
1411
1525
|
console.log(" bundle-ignore <id> - Ignore a bundle");
|
|
1412
1526
|
console.log(" bundle-auto - Auto-dispatch high-priority bundles");
|
|
1413
|
-
console.log("═".repeat(60)
|
|
1527
|
+
console.log(`${"═".repeat(60)}\n`);
|
|
1414
1528
|
}
|
|
1415
1529
|
// ============================================================================
|
|
1416
1530
|
// Commands
|
|
@@ -1418,7 +1532,7 @@ function printStatus(state) {
|
|
|
1418
1532
|
function findMergeBySha(state, prNumOrSha) {
|
|
1419
1533
|
// Try PR number first
|
|
1420
1534
|
const prNum = parseInt(prNumOrSha, 10);
|
|
1421
|
-
if (!isNaN(prNum)) {
|
|
1535
|
+
if (!Number.isNaN(prNum)) {
|
|
1422
1536
|
for (const [sha, entry] of Object.entries(state.merges)) {
|
|
1423
1537
|
if (entry.prNumber === prNum) {
|
|
1424
1538
|
return { sha, entry };
|
|
@@ -1437,13 +1551,13 @@ function findMergeBySha(state, prNumOrSha) {
|
|
|
1437
1551
|
}
|
|
1438
1552
|
return null;
|
|
1439
1553
|
}
|
|
1440
|
-
function
|
|
1554
|
+
function _cmdMerge(state, prNumOrSha) {
|
|
1441
1555
|
const found = findMergeBySha(state, prNumOrSha);
|
|
1442
1556
|
if (!found) {
|
|
1443
1557
|
console.error(`❌ PR not found: ${prNumOrSha}`);
|
|
1444
1558
|
return;
|
|
1445
1559
|
}
|
|
1446
|
-
const {
|
|
1560
|
+
const { entry } = found;
|
|
1447
1561
|
if (entry.status !== "pending_review") {
|
|
1448
1562
|
console.error(`❌ PR #${entry.prNumber} is not ready for review (status: ${entry.status})`);
|
|
1449
1563
|
return;
|
|
@@ -1466,7 +1580,7 @@ function cmdMerge(state, prNumOrSha) {
|
|
|
1466
1580
|
}
|
|
1467
1581
|
}
|
|
1468
1582
|
catch (e) {
|
|
1469
|
-
console.error(`❌ Merge failed: ${e}`);
|
|
1583
|
+
console.error(`❌ Merge failed: ${String(e)}`);
|
|
1470
1584
|
}
|
|
1471
1585
|
}
|
|
1472
1586
|
function cmdIgnore(state, prNumOrSha, reason) {
|
|
@@ -1526,7 +1640,7 @@ function cmdShow(state, prNumOrSha) {
|
|
|
1526
1640
|
console.log(diff);
|
|
1527
1641
|
}
|
|
1528
1642
|
catch (e) {
|
|
1529
|
-
console.error(` Could not get diff: ${e}`);
|
|
1643
|
+
console.error(` Could not get diff: ${String(e)}`);
|
|
1530
1644
|
}
|
|
1531
1645
|
}
|
|
1532
1646
|
function cmdShelve(state, prNumOrSha) {
|
|
@@ -1571,7 +1685,7 @@ function cmdUnshelve(state, prNumOrSha) {
|
|
|
1571
1685
|
saveState(state);
|
|
1572
1686
|
console.log(`👀 PR #${entry.prNumber} moved back to pending review`);
|
|
1573
1687
|
}
|
|
1574
|
-
function generatePrDescription(state, sha,
|
|
1688
|
+
function generatePrDescription(state, sha, _entry) {
|
|
1575
1689
|
const upstreamPath = getUpstreamPath(state);
|
|
1576
1690
|
try {
|
|
1577
1691
|
// Get commit message body for description
|
|
@@ -1593,7 +1707,7 @@ function cmdTimeline(state) {
|
|
|
1593
1707
|
const entries = Object.entries(state.merges);
|
|
1594
1708
|
// Group by date
|
|
1595
1709
|
const byDate = new Map();
|
|
1596
|
-
for (const [
|
|
1710
|
+
for (const [_sha, entry] of entries) {
|
|
1597
1711
|
const date = entry.date.split("T")[0];
|
|
1598
1712
|
if (!byDate.has(date)) {
|
|
1599
1713
|
byDate.set(date, {
|
|
@@ -1607,6 +1721,8 @@ function cmdTimeline(state) {
|
|
|
1607
1721
|
});
|
|
1608
1722
|
}
|
|
1609
1723
|
const day = byDate.get(date);
|
|
1724
|
+
if (!day)
|
|
1725
|
+
continue;
|
|
1610
1726
|
if (entry.status === "merged")
|
|
1611
1727
|
day.merged++;
|
|
1612
1728
|
else if (entry.status === "porting")
|
|
@@ -1625,7 +1741,15 @@ function cmdTimeline(state) {
|
|
|
1625
1741
|
// Sort dates
|
|
1626
1742
|
const dates = [...byDate.keys()].sort();
|
|
1627
1743
|
// Calculate totals
|
|
1628
|
-
const totals = {
|
|
1744
|
+
const totals = {
|
|
1745
|
+
merged: 0,
|
|
1746
|
+
porting: 0,
|
|
1747
|
+
pending: 0,
|
|
1748
|
+
new: 0,
|
|
1749
|
+
ignored: 0,
|
|
1750
|
+
prs: 0,
|
|
1751
|
+
direct: 0,
|
|
1752
|
+
};
|
|
1629
1753
|
for (const day of byDate.values()) {
|
|
1630
1754
|
totals.merged += day.merged;
|
|
1631
1755
|
totals.porting += day.porting;
|
|
@@ -1635,11 +1759,15 @@ function cmdTimeline(state) {
|
|
|
1635
1759
|
totals.prs += day.prs;
|
|
1636
1760
|
totals.direct += day.direct;
|
|
1637
1761
|
}
|
|
1638
|
-
const totalCommits = totals.merged +
|
|
1762
|
+
const totalCommits = totals.merged +
|
|
1763
|
+
totals.porting +
|
|
1764
|
+
totals.pending +
|
|
1765
|
+
totals.new +
|
|
1766
|
+
totals.ignored;
|
|
1639
1767
|
const ported = totals.merged + totals.porting + totals.pending;
|
|
1640
1768
|
const portedPct = Math.round((ported / totalCommits) * 100);
|
|
1641
1769
|
// Header
|
|
1642
|
-
console.log(
|
|
1770
|
+
console.log(`\n${"═".repeat(80)}`);
|
|
1643
1771
|
console.log(" 📊 NEXUS ← UPSTREAM SYNC TIMELINE");
|
|
1644
1772
|
console.log("═".repeat(80));
|
|
1645
1773
|
console.log(` Tracking since: ${state.trackingStartDate}`);
|
|
@@ -1665,11 +1793,13 @@ function cmdTimeline(state) {
|
|
|
1665
1793
|
console.log("");
|
|
1666
1794
|
// Daily timeline
|
|
1667
1795
|
console.log(" DAILY TIMELINE:");
|
|
1668
|
-
console.log(
|
|
1796
|
+
console.log(` ${"─".repeat(76)}`);
|
|
1669
1797
|
console.log(" Date │ Total │ Merged │ Porting │ Pending │ New │ Progress");
|
|
1670
|
-
console.log(
|
|
1798
|
+
console.log(` ${"─".repeat(76)}`);
|
|
1671
1799
|
for (const date of dates) {
|
|
1672
1800
|
const day = byDate.get(date);
|
|
1801
|
+
if (!day)
|
|
1802
|
+
continue;
|
|
1673
1803
|
const dayTotal = day.merged + day.porting + day.pending + day.new + day.ignored;
|
|
1674
1804
|
const dayPorted = day.merged + day.porting + day.pending;
|
|
1675
1805
|
const dayPct = dayTotal > 0 ? Math.round((dayPorted / dayTotal) * 100) : 0;
|
|
@@ -1685,7 +1815,7 @@ function cmdTimeline(state) {
|
|
|
1685
1815
|
"·".repeat(Math.max(0, miniNew));
|
|
1686
1816
|
console.log(` ${date} │ ${String(dayTotal).padStart(5)} │ ${String(day.merged).padStart(6)} │ ${String(day.porting).padStart(7)} │ ${String(day.pending).padStart(7)} │ ${String(day.new).padStart(5)} │ ${miniBar} ${dayPct}%`);
|
|
1687
1817
|
}
|
|
1688
|
-
console.log(
|
|
1818
|
+
console.log(` ${"─".repeat(76)}`);
|
|
1689
1819
|
console.log(` TOTAL │ ${String(totalCommits).padStart(5)} │ ${String(totals.merged).padStart(6)} │ ${String(totals.porting).padStart(7)} │ ${String(totals.pending).padStart(7)} │ ${String(totals.new).padStart(5)} │`);
|
|
1690
1820
|
console.log("");
|
|
1691
1821
|
// Bundle summary
|
|
@@ -1704,7 +1834,7 @@ function cmdTimeline(state) {
|
|
|
1704
1834
|
const portingEntries = entries.filter(([_, e]) => e.status === "porting");
|
|
1705
1835
|
if (portingEntries.length > 0) {
|
|
1706
1836
|
console.log(" 🔧 CURRENTLY PORTING:");
|
|
1707
|
-
for (const [
|
|
1837
|
+
for (const [_sha, entry] of portingEntries.slice(0, 8)) {
|
|
1708
1838
|
const type = entry.isDirectCommit ? "DIRECT" : `PR #${entry.prNumber}`;
|
|
1709
1839
|
const session = entry.tmuxSession ? ` [${entry.tmuxSession}]` : "";
|
|
1710
1840
|
console.log(` ${type}: ${entry.title.slice(0, 50)}...${session}`);
|
|
@@ -1728,7 +1858,7 @@ function cmdTimeline(state) {
|
|
|
1728
1858
|
console.log(` But Peter pushes ${totals.direct} commits directly to main (no PRs).`);
|
|
1729
1859
|
console.log(` We just discovered these today - that's why there's so much "new" work.`);
|
|
1730
1860
|
console.log("");
|
|
1731
|
-
console.log("═".repeat(80)
|
|
1861
|
+
console.log(`${"═".repeat(80)}\n`);
|
|
1732
1862
|
}
|
|
1733
1863
|
function cmdReview(state) {
|
|
1734
1864
|
const reviewable = Object.entries(state.merges)
|
|
@@ -1853,7 +1983,7 @@ function cmdRebundle(state) {
|
|
|
1853
1983
|
}
|
|
1854
1984
|
// 1. Clear bundleIds from all "new" direct commits (not porting ones)
|
|
1855
1985
|
let clearedCommits = 0;
|
|
1856
|
-
for (const [
|
|
1986
|
+
for (const [_sha, entry] of Object.entries(state.merges)) {
|
|
1857
1987
|
if (entry.isDirectCommit && entry.bundleId && entry.status === "new") {
|
|
1858
1988
|
delete entry.bundleId;
|
|
1859
1989
|
clearedCommits++;
|
|
@@ -1997,7 +2127,7 @@ function dispatchBundleAgent(state, bundleId) {
|
|
|
1997
2127
|
exec(`git worktree add ${worktreePath} ${branchName}`, PROJECT_ROOT);
|
|
1998
2128
|
}
|
|
1999
2129
|
catch {
|
|
2000
|
-
throw new Error(`Failed to create worktree: ${e}`);
|
|
2130
|
+
throw new Error(`Failed to create worktree: ${String(e)}`);
|
|
2001
2131
|
}
|
|
2002
2132
|
}
|
|
2003
2133
|
}
|
|
@@ -2009,7 +2139,12 @@ function dispatchBundleAgent(state, bundleId) {
|
|
|
2009
2139
|
const runId = `bundle-${shortId}-${Date.now()}`;
|
|
2010
2140
|
const prompt = "Read PORT_TASK.md and complete the porting task. This is a BUNDLED port - apply all changes as one cohesive update. Commit your changes when done (a supervisor will also attempt to commit).";
|
|
2011
2141
|
const commitMessage = `feat: port ${bundle.name}`;
|
|
2012
|
-
const run = startDetachedAgentRun({
|
|
2142
|
+
const run = startDetachedAgentRun({
|
|
2143
|
+
runId,
|
|
2144
|
+
cwd: worktreePath,
|
|
2145
|
+
prompt,
|
|
2146
|
+
commitMessage,
|
|
2147
|
+
});
|
|
2013
2148
|
console.log(` Started detached runner pid=${run.pid}`);
|
|
2014
2149
|
// Update bundle status
|
|
2015
2150
|
bundle.status = "porting";
|
|
@@ -2054,7 +2189,7 @@ function cmdBundleDispatch(state, bundleId) {
|
|
|
2054
2189
|
dispatchBundleAgent(state, found);
|
|
2055
2190
|
}
|
|
2056
2191
|
catch (e) {
|
|
2057
|
-
console.error(`❌ Failed to dispatch: ${e}`);
|
|
2192
|
+
console.error(`❌ Failed to dispatch: ${String(e)}`);
|
|
2058
2193
|
}
|
|
2059
2194
|
}
|
|
2060
2195
|
function cmdBundleIgnore(state, bundleId, reason) {
|
|
@@ -2143,7 +2278,9 @@ function cmdBundleSplit(state, bundleId) {
|
|
|
2143
2278
|
const types = new Set(sorted.map((c) => c.parsed.type));
|
|
2144
2279
|
const scopes = new Set(sorted.map((c) => c.parsed.scope).filter(Boolean));
|
|
2145
2280
|
const authors = new Set(sorted.map((c) => c.entry.author).filter(Boolean));
|
|
2146
|
-
const author = authors.size === 1
|
|
2281
|
+
const author = authors.size === 1
|
|
2282
|
+
? Array.from(authors)[0]
|
|
2283
|
+
: bundle.author || "multiple";
|
|
2147
2284
|
const dateLabel = firstEntry?.date?.split("T")[0] ?? "unknown";
|
|
2148
2285
|
const name = sorted.length === 1
|
|
2149
2286
|
? sorted[0]?.entry.title
|
|
@@ -2233,7 +2370,8 @@ function cmdBundleAuto(state, minPriority = 70) {
|
|
|
2233
2370
|
console.log("🔍 Analyzing direct commits first...\n");
|
|
2234
2371
|
cmdBundles(state);
|
|
2235
2372
|
}
|
|
2236
|
-
const
|
|
2373
|
+
const bundles = state.bundles ?? {};
|
|
2374
|
+
const pending = Object.values(bundles)
|
|
2237
2375
|
.filter((b) => b.status === "pending" && b.priority >= minPriority)
|
|
2238
2376
|
.sort((a, b) => b.priority - a.priority);
|
|
2239
2377
|
if (pending.length === 0) {
|
|
@@ -2241,8 +2379,8 @@ function cmdBundleAuto(state, minPriority = 70) {
|
|
|
2241
2379
|
return;
|
|
2242
2380
|
}
|
|
2243
2381
|
// Check how many agents are already running
|
|
2244
|
-
const currentlyRunning = Object.values(
|
|
2245
|
-
const maxConcurrent =
|
|
2382
|
+
const currentlyRunning = Object.values(bundles).filter((b) => b.status === "porting").length;
|
|
2383
|
+
const maxConcurrent = getMaxConcurrent();
|
|
2246
2384
|
const canDispatch = Math.max(0, maxConcurrent - currentlyRunning);
|
|
2247
2385
|
if (canDispatch === 0) {
|
|
2248
2386
|
console.log(`⏳ Max agents (${maxConcurrent}) already running. Wait for some to complete.`);
|
|
@@ -2251,13 +2389,17 @@ function cmdBundleAuto(state, minPriority = 70) {
|
|
|
2251
2389
|
const toDispatch = pending.slice(0, canDispatch);
|
|
2252
2390
|
console.log(`🚀 Auto-dispatching ${toDispatch.length} high-priority bundle(s)...\n`);
|
|
2253
2391
|
for (const bundle of toDispatch) {
|
|
2254
|
-
const priorityLabel = bundle.priority >= 100
|
|
2392
|
+
const priorityLabel = bundle.priority >= 100
|
|
2393
|
+
? "🔥 BREAKING"
|
|
2394
|
+
: bundle.priority >= 70
|
|
2395
|
+
? "✨ FEATURE"
|
|
2396
|
+
: "🐛 FIX";
|
|
2255
2397
|
console.log(`\n${priorityLabel} ${bundle.name}`);
|
|
2256
2398
|
try {
|
|
2257
2399
|
dispatchBundleAgent(state, bundle.id);
|
|
2258
2400
|
}
|
|
2259
2401
|
catch (e) {
|
|
2260
|
-
console.error(` ❌ Failed: ${e}`);
|
|
2402
|
+
console.error(` ❌ Failed: ${String(e)}`);
|
|
2261
2403
|
}
|
|
2262
2404
|
}
|
|
2263
2405
|
console.log(`\n✅ Dispatched ${toDispatch.length} bundle(s).`);
|
|
@@ -2279,13 +2421,14 @@ function cmdBundleDispatchOldest(state, requested) {
|
|
|
2279
2421
|
: 0;
|
|
2280
2422
|
const portingIndividual = Object.values(state.merges).filter((e) => e.status === "porting" && !e.bundleId).length;
|
|
2281
2423
|
const currentlyPorting = portingBundles + portingIndividual;
|
|
2282
|
-
const maxConcurrent =
|
|
2424
|
+
const maxConcurrent = getMaxConcurrent();
|
|
2283
2425
|
const available = Math.max(0, maxConcurrent - currentlyPorting);
|
|
2284
2426
|
if (available === 0) {
|
|
2285
2427
|
console.log(`⏳ All ${maxConcurrent} slots occupied. Wait for current bundles to finish.`);
|
|
2286
2428
|
return;
|
|
2287
2429
|
}
|
|
2288
|
-
const
|
|
2430
|
+
const bundles = state.bundles ?? {};
|
|
2431
|
+
const pending = Object.values(bundles)
|
|
2289
2432
|
.filter((b) => b.status === "pending")
|
|
2290
2433
|
.sort((a, b) => {
|
|
2291
2434
|
const dateDiff = a.dateRange.start.localeCompare(b.dateRange.start);
|
|
@@ -2308,7 +2451,7 @@ function cmdBundleDispatchOldest(state, requested) {
|
|
|
2308
2451
|
console.log(` ✓ ${bundle.name} (${bundle.commits.length} commits)`);
|
|
2309
2452
|
}
|
|
2310
2453
|
catch (e) {
|
|
2311
|
-
console.error(` ✗ ${bundle.id}: ${e}`);
|
|
2454
|
+
console.error(` ✗ ${bundle.id}: ${String(e)}`);
|
|
2312
2455
|
}
|
|
2313
2456
|
}
|
|
2314
2457
|
}
|
|
@@ -2326,7 +2469,7 @@ function checkMergeConflicts(state, prNums) {
|
|
|
2326
2469
|
exec(`git merge --no-commit --no-ff ${found.entry.portBranch}`, PROJECT_ROOT);
|
|
2327
2470
|
exec(`git reset --hard HEAD`, PROJECT_ROOT);
|
|
2328
2471
|
}
|
|
2329
|
-
catch
|
|
2472
|
+
catch {
|
|
2330
2473
|
// Get conflicting files
|
|
2331
2474
|
try {
|
|
2332
2475
|
const conflictFiles = exec(`git diff --name-only --diff-filter=U`, PROJECT_ROOT);
|
|
@@ -2355,7 +2498,9 @@ function checkMergeConflicts(state, prNums) {
|
|
|
2355
2498
|
return { canMerge: conflicts.length === 0, conflicts };
|
|
2356
2499
|
}
|
|
2357
2500
|
function cmdMergeMultiple(state, prNums) {
|
|
2358
|
-
const nums = prNums
|
|
2501
|
+
const nums = prNums
|
|
2502
|
+
.map((p) => parseInt(p, 10))
|
|
2503
|
+
.filter((n) => !Number.isNaN(n));
|
|
2359
2504
|
if (nums.length === 0) {
|
|
2360
2505
|
console.error("Usage: upstream-sync merge <pr#> [pr#] [pr#]...");
|
|
2361
2506
|
return;
|
|
@@ -2368,7 +2513,8 @@ function cmdMergeMultiple(state, prNums) {
|
|
|
2368
2513
|
console.error(`❌ PR #${prNum} not found`);
|
|
2369
2514
|
return;
|
|
2370
2515
|
}
|
|
2371
|
-
if (found.entry.status !== "pending_review" &&
|
|
2516
|
+
if (found.entry.status !== "pending_review" &&
|
|
2517
|
+
found.entry.status !== "shelved") {
|
|
2372
2518
|
console.error(`❌ PR #${prNum} is not ready for merge (status: ${found.entry.status})`);
|
|
2373
2519
|
return;
|
|
2374
2520
|
}
|
|
@@ -2415,7 +2561,7 @@ function cmdMergeMultiple(state, prNums) {
|
|
|
2415
2561
|
}
|
|
2416
2562
|
}
|
|
2417
2563
|
catch (e) {
|
|
2418
|
-
console.error(` ❌ Merge failed: ${e}`);
|
|
2564
|
+
console.error(` ❌ Merge failed: ${String(e)}`);
|
|
2419
2565
|
console.log(` Stopping merge sequence. Please resolve manually.`);
|
|
2420
2566
|
return;
|
|
2421
2567
|
}
|
|
@@ -2433,7 +2579,7 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2433
2579
|
console.log(` Interval: ${intervalMinutes} minute(s)`);
|
|
2434
2580
|
console.log(` Max concurrent agents: 6`);
|
|
2435
2581
|
console.log(` Press Ctrl+C to stop\n`);
|
|
2436
|
-
console.log("═".repeat(60)
|
|
2582
|
+
console.log(`${"═".repeat(60)}\n`);
|
|
2437
2583
|
let iteration = 0;
|
|
2438
2584
|
while (true) {
|
|
2439
2585
|
iteration++;
|
|
@@ -2445,7 +2591,7 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2445
2591
|
state = loadState();
|
|
2446
2592
|
}
|
|
2447
2593
|
catch (e) {
|
|
2448
|
-
console.error(` ❌ Failed to load state: ${e}`);
|
|
2594
|
+
console.error(` ❌ Failed to load state: ${String(e)}`);
|
|
2449
2595
|
await sleep(intervalMinutes * 60 * 1000);
|
|
2450
2596
|
continue;
|
|
2451
2597
|
}
|
|
@@ -2454,7 +2600,7 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2454
2600
|
fetchUpstream(state);
|
|
2455
2601
|
}
|
|
2456
2602
|
catch (e) {
|
|
2457
|
-
console.error(` ⚠️ Fetch failed: ${e}`);
|
|
2603
|
+
console.error(` ⚠️ Fetch failed: ${String(e)}`);
|
|
2458
2604
|
}
|
|
2459
2605
|
// 2. Find new commits (both merges and direct commits)
|
|
2460
2606
|
const newCommits = findNewCommits(state);
|
|
@@ -2498,7 +2644,13 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2498
2644
|
for (const entry of Object.values(state.merges)) {
|
|
2499
2645
|
mergeCounts[entry.status]++;
|
|
2500
2646
|
}
|
|
2501
|
-
const bundleCounts = {
|
|
2647
|
+
const bundleCounts = {
|
|
2648
|
+
pending: 0,
|
|
2649
|
+
porting: 0,
|
|
2650
|
+
pending_review: 0,
|
|
2651
|
+
merged: 0,
|
|
2652
|
+
ignored: 0,
|
|
2653
|
+
};
|
|
2502
2654
|
if (state.bundles) {
|
|
2503
2655
|
for (const bundle of Object.values(state.bundles)) {
|
|
2504
2656
|
if (bundle.status in bundleCounts) {
|
|
@@ -2514,22 +2666,23 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2514
2666
|
console.log(` 📊 Status: ${totalPorting} porting | ${totalReady} ready | ${totalQueued} queued (${bundleCounts.pending} bundles + ${totalQueued - bundleCounts.pending} PRs)`);
|
|
2515
2667
|
// 5. Check if we're done
|
|
2516
2668
|
if (totalQueued === 0 && totalPorting === 0) {
|
|
2517
|
-
console.log(
|
|
2669
|
+
console.log(`\n${"═".repeat(60)}`);
|
|
2518
2670
|
console.log("🎉 ALL DONE! No more work to process.");
|
|
2519
2671
|
console.log(` ✅ Merged: ${mergeCounts.merged}`);
|
|
2520
2672
|
console.log(` 👀 Ready for review: ${totalReady}`);
|
|
2521
2673
|
console.log(` 📦 Shelved: ${mergeCounts.shelved}`);
|
|
2522
2674
|
console.log(` ⏭️ Ignored: ${mergeCounts.ignored}`);
|
|
2523
|
-
console.log("═".repeat(60)
|
|
2675
|
+
console.log(`${"═".repeat(60)}\n`);
|
|
2524
2676
|
console.log("Daemon exiting. Run 'upstream-sync review' to see pending PRs.");
|
|
2525
2677
|
break;
|
|
2526
2678
|
}
|
|
2527
2679
|
// 6. Dispatch agents for new work (up to max concurrent)
|
|
2528
2680
|
// Handle both bundles (for direct commits) and individual PRs
|
|
2529
|
-
const maxConcurrent =
|
|
2681
|
+
const maxConcurrent = getMaxConcurrent();
|
|
2530
2682
|
// Count currently porting (both bundle and individual)
|
|
2531
2683
|
const portingBundles = state.bundles
|
|
2532
|
-
? Object.values(state.bundles).filter((b) => b.status === "porting")
|
|
2684
|
+
? Object.values(state.bundles).filter((b) => b.status === "porting")
|
|
2685
|
+
.length
|
|
2533
2686
|
: 0;
|
|
2534
2687
|
const portingIndividual = Object.values(state.merges).filter((e) => e.status === "porting" && !e.bundleId).length;
|
|
2535
2688
|
const currentlyPorting = portingBundles + portingIndividual;
|
|
@@ -2545,7 +2698,8 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2545
2698
|
.filter(([_, b]) => b.status === "pending")
|
|
2546
2699
|
.sort((a, b) => {
|
|
2547
2700
|
// Oldest first, then higher priority
|
|
2548
|
-
const dateDiff = new Date(a[1].dateRange.start).getTime() -
|
|
2701
|
+
const dateDiff = new Date(a[1].dateRange.start).getTime() -
|
|
2702
|
+
new Date(b[1].dateRange.start).getTime();
|
|
2549
2703
|
if (dateDiff !== 0)
|
|
2550
2704
|
return dateDiff;
|
|
2551
2705
|
return b[1].priority - a[1].priority;
|
|
@@ -2571,7 +2725,7 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2571
2725
|
dispatched++;
|
|
2572
2726
|
}
|
|
2573
2727
|
catch (e) {
|
|
2574
|
-
console.error(` ✗ Bundle ${bundleId}: ${e}`);
|
|
2728
|
+
console.error(` ✗ Bundle ${bundleId}: ${String(e)}`);
|
|
2575
2729
|
}
|
|
2576
2730
|
}
|
|
2577
2731
|
}
|
|
@@ -2581,7 +2735,7 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2581
2735
|
.filter(([_, e]) => e.status === "new" && !e.isDirectCommit && !e.bundleId)
|
|
2582
2736
|
.sort((a, b) => {
|
|
2583
2737
|
// Oldest first
|
|
2584
|
-
return new Date(a[1].date).getTime() - new Date(b[1].date).getTime();
|
|
2738
|
+
return (new Date(a[1].date).getTime() - new Date(b[1].date).getTime());
|
|
2585
2739
|
});
|
|
2586
2740
|
for (const [sha, entry] of newPRs) {
|
|
2587
2741
|
if (dispatched >= canDispatch)
|
|
@@ -2592,7 +2746,7 @@ async function runDaemon(state, intervalMinutes = 2) {
|
|
|
2592
2746
|
dispatched++;
|
|
2593
2747
|
}
|
|
2594
2748
|
catch (e) {
|
|
2595
|
-
console.error(` ✗ PR #${entry.prNumber}: ${e}`);
|
|
2749
|
+
console.error(` ✗ PR #${entry.prNumber}: ${String(e)}`);
|
|
2596
2750
|
}
|
|
2597
2751
|
}
|
|
2598
2752
|
}
|
|
@@ -2625,7 +2779,7 @@ async function main() {
|
|
|
2625
2779
|
state = loadState();
|
|
2626
2780
|
}
|
|
2627
2781
|
catch (e) {
|
|
2628
|
-
console.error(`❌ ${e}`);
|
|
2782
|
+
console.error(`❌ ${String(e)}`);
|
|
2629
2783
|
process.exit(1);
|
|
2630
2784
|
}
|
|
2631
2785
|
switch (command) {
|
|
@@ -2726,11 +2880,12 @@ async function main() {
|
|
|
2726
2880
|
cmdBundleDispatchOldest(state, Number.isFinite(rawCount) ? rawCount : undefined);
|
|
2727
2881
|
break;
|
|
2728
2882
|
}
|
|
2729
|
-
case "bundle-auto":
|
|
2883
|
+
case "bundle-auto": {
|
|
2730
2884
|
// Optional: specify minimum priority (default 70 = features+)
|
|
2731
2885
|
const minPriority = args[1] ? parseInt(args[1], 10) : 70;
|
|
2732
2886
|
cmdBundleAuto(state, minPriority);
|
|
2733
2887
|
break;
|
|
2888
|
+
}
|
|
2734
2889
|
case "pause":
|
|
2735
2890
|
state.dispatchDisabled = true;
|
|
2736
2891
|
saveState(state);
|
|
@@ -2741,8 +2896,7 @@ async function main() {
|
|
|
2741
2896
|
saveState(state);
|
|
2742
2897
|
console.log("✅ Dispatch resumed. New agents can be started.");
|
|
2743
2898
|
break;
|
|
2744
|
-
|
|
2745
|
-
default:
|
|
2899
|
+
default: {
|
|
2746
2900
|
// Full sync flow
|
|
2747
2901
|
console.log("🔄 UPSTREAM SYNC\n");
|
|
2748
2902
|
// 1. Fetch
|
|
@@ -2793,7 +2947,7 @@ async function main() {
|
|
|
2793
2947
|
updateAllStatuses(state);
|
|
2794
2948
|
// 4. Dispatch agents - PRIORITIZE direct commits
|
|
2795
2949
|
const currentlyPorting = Object.values(state.merges).filter((e) => e.status === "porting").length;
|
|
2796
|
-
const maxConcurrentSync =
|
|
2950
|
+
const maxConcurrentSync = getMaxConcurrent();
|
|
2797
2951
|
const canDispatchSync = Math.max(0, maxConcurrentSync - currentlyPorting);
|
|
2798
2952
|
// Sort: direct commits first, then by date
|
|
2799
2953
|
const newEntries = Object.entries(state.merges)
|
|
@@ -2811,11 +2965,13 @@ async function main() {
|
|
|
2811
2965
|
for (const [sha, entry] of toDispatchSync) {
|
|
2812
2966
|
try {
|
|
2813
2967
|
dispatchAgent(state, sha);
|
|
2814
|
-
const label = entry.isDirectCommit
|
|
2968
|
+
const label = entry.isDirectCommit
|
|
2969
|
+
? "⭐ DIRECT"
|
|
2970
|
+
: `PR #${entry.prNumber}`;
|
|
2815
2971
|
console.log(` ✓ ${label}: ${entry.title}`);
|
|
2816
2972
|
}
|
|
2817
2973
|
catch (e) {
|
|
2818
|
-
console.error(` Failed to dispatch: ${e}`);
|
|
2974
|
+
console.error(` Failed to dispatch: ${String(e)}`);
|
|
2819
2975
|
}
|
|
2820
2976
|
}
|
|
2821
2977
|
}
|
|
@@ -2825,6 +2981,7 @@ async function main() {
|
|
|
2825
2981
|
// 5. Show status
|
|
2826
2982
|
printStatus(state);
|
|
2827
2983
|
break;
|
|
2984
|
+
}
|
|
2828
2985
|
}
|
|
2829
2986
|
}
|
|
2830
2987
|
main().catch((e) => {
|