@amsterdamdatalabs/adl-agent-control-linux-x64 0.1.37 → 0.1.47
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/bin/adl +2 -2
- package/bin/reconcile +10 -13
- package/bin/setup +15 -15
- package/package.json +1 -1
- package/packages/agent-hooks/package.json +1 -1
- package/packages/agent-hooks/src/main.ts +23 -2
- package/packages/agent-hooks/src/rules/impl/adl-mcp-cli.ts +12 -8
- package/packages/agent-hooks/src/rules/impl/adl-run-required.ts +11 -5
- package/packages/agent-hooks/src/rules/impl/adl-workitem-role.ts +18 -5
- package/packages/agent-hooks/src/rules/impl/agent-tamper-protection.ts +30 -26
- package/packages/agent-hooks/src/rules/impl/git-branch.ts +1 -1
- package/packages/agent-hooks/src/rules/impl/git-target.ts +5 -0
- package/packages/agent-hooks/src/rules/impl/mcp-tool.ts +3 -1
- package/packages/agent-hooks/src/rules/impl/tool-frontend.ts +230 -11
- package/packages/agent-hooks/src/rules/shell/git-identity.ts +1 -1
- package/packages/agent-hooks/src/rules/shell/invocation.ts +14 -2
- package/packages/agent-hooks/src/rules/shell/protected-carriers.ts +10 -11
- package/packages/agent-settings/assets/TARUN-GLOBAL-AGENTS-INSTRUCTIONS.md +2 -2
- package/packages/agent-settings/package.json +1 -1
- package/packages/agent-settings/src/deploy/agents-deploy.ts +19 -1
- package/packages/agent-settings/src/deploy/index.ts +1 -0
- package/packages/agent-skills/assets/agent-search-tool-audit/SKILL.md +4 -6
- package/packages/agent-skills/assets/finalize-feature/SKILL.md +3 -3
- package/packages/agent-skills/assets/implement-feature/SKILL.md +17 -20
- package/packages/agent-skills/assets/implement-feature/references/briefs.md +3 -3
- package/packages/agent-skills/assets/implement-item/SKILL.md +3 -3
- package/packages/agent-skills/assets/mechanics/SKILL.md +1 -1
- package/packages/agent-skills/assets/plan-feature/SKILL.md +10 -10
- package/packages/agent-skills/assets/prepare-promotion/SKILL.md +3 -4
- package/packages/agent-skills/assets/report-bug/SKILL.md +2 -2
- package/packages/agent-skills/assets/review-change/SKILL.md +3 -3
- package/packages/agent-skills/assets/review-plan/SKILL.md +4 -4
- package/packages/agent-skills/assets/triage-workitems/SKILL.md +4 -4
- package/packages/agent-skills/assets/validate-design/SKILL.md +6 -12
- package/packages/agent-skills/assets/verify-item/SKILL.md +5 -5
- package/packages/agent-skills/assets/wayfinder/SKILL.md +1 -1
- package/packages/agent-skills/assets/workitems-process/SKILL.md +19 -21
- package/packages/agent-skills/assets/workitems-process/references/ado-and-git.md +36 -3
- package/packages/agent-skills/assets/workitems-process/references/evidence.md +4 -4
- package/packages/agent-skills/assets/write-feature/SKILL.md +15 -18
- package/packages/agent-skills/dist/deploy.d.ts +45 -0
- package/packages/agent-skills/dist/deploy.d.ts.map +1 -1
- package/packages/agent-skills/dist/deploy.js +167 -3
- package/packages/agent-skills/dist/deploy.js.map +1 -1
- package/packages/agent-skills/dist/index.d.ts +1 -1
- package/packages/agent-skills/dist/index.d.ts.map +1 -1
- package/packages/agent-skills/dist/index.js +1 -1
- package/packages/agent-skills/dist/index.js.map +1 -1
- package/packages/agent-skills/package.json +1 -1
- package/packages/agent-skills/src/deploy.ts +241 -3
- package/packages/agent-skills/src/index.ts +10 -1
- package/packages/azdo/README.md +13 -4
- package/packages/azdo/bunfig.toml +6 -0
- package/packages/azdo/package.json +1 -1
- package/packages/azdo/src/azdo-attachments.ts +2 -1
- package/packages/azdo/src/azdo-pr.ts +182 -6
- package/packages/azdo/src/azdo-wit.ts +290 -41
- package/packages/azdo/src/cli.ts +79 -12
- package/packages/azdo/src/lib/client.ts +88 -16
- package/packages/azdo/src/mcp-server.ts +75 -10
- package/packages/azdo/src/tool-dispatch.ts +61 -26
- package/packages/fleet/src/install-adl-package.ts +4 -3
- package/packages/repo-controls/assets/promote.ts +1 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/no-type.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-experiment.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-profile-untyped/docs/research/typed-research.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-key.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/extra-keys.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-created.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-description.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-last-update.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-status.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/missing-title.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/optional-keys.md +1 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-research/docs/research/research-ok.md +1 -1
- package/packages/repo-controls/package.json +1 -1
- package/packages/repo-controls/src/config.ts +21 -2
- package/packages/repo-controls/src/doctor.ts +9 -14
- package/packages/repo-controls/src/findings-ratchet.ts +12 -0
- package/packages/repo-controls/src/hook.ts +61 -4
- package/bin/lean-ctx-observe-strip +0 -434
- package/bin/lib/lean-ctx-observe-hooks.sh +0 -634
- package/bin/update-azure-agent-pool +0 -29
- package/packages/agent-hooks/src/hooks/lean-ctx/handler.ts +0 -22
- package/packages/agent-hooks/src/hooks/lean-ctx/index.ts +0 -173
- package/packages/agent-hooks/src/hooks/lean-ctx/mapping.ts +0 -121
- package/packages/agent-hooks/src/hooks/lean-ctx/scope-rewrite.ts +0 -207
- package/packages/agent-hooks/src/lean-ctx/env.ts +0 -123
- package/packages/agent-hooks/src/lean-ctx/mcp.ts +0 -142
- package/packages/agent-hooks/src/rules/impl/adl-build-run-denied.ts +0 -51
- package/packages/agent-hooks/src/rules/impl/adl-session-required.ts +0 -403
- package/packages/agent-hooks/src/rules/impl/tool-lean-ctx-global.ts +0 -594
- package/packages/agent-settings/assets/GLOBAL-AGENTS.md +0 -112
- package/packages/agent-settings/assets/lean-ctx/config.toml +0 -42
- package/packages/agent-settings/assets/lean-ctx/gitignore +0 -10
- package/packages/agent-settings/assets/lean-ctx/layout.toml +0 -7
- package/packages/agent-settings/src/deploy/lean-ctx-deploy.ts +0 -230
- package/packages/agent-settings/src/deploy/lean-ctx-merge.ts +0 -580
- package/packages/agent-skills/assets/adl-mechanics/SKILL.md +0 -38
- package/packages/agent-skills/assets/codebase-design/SKILL.md +0 -98
- package/packages/agent-skills/assets/codebase-design/references/DEEPENING.md +0 -37
- package/packages/agent-skills/assets/codebase-design/references/DESIGN-IT-TWICE.md +0 -44
- package/packages/agent-skills/assets/codebase-design/references/TESTABILITY.md +0 -29
- package/packages/agent-skills/assets/debug-issue/SKILL.md +0 -41
- package/packages/agent-skills/assets/domain-modeling/SKILL.md +0 -86
- package/packages/agent-skills/assets/domain-modeling/references/ADR-FORMAT.md +0 -47
- package/packages/agent-skills/assets/domain-modeling/references/CONTEXT-FORMAT.md +0 -60
- package/packages/agent-skills/assets/implement/SKILL.md +0 -28
- package/packages/agent-skills/assets/review-changes/SKILL.md +0 -45
- package/packages/agent-skills/assets/tdd/SKILL.md +0 -49
- package/packages/agent-skills/assets/tdd/references/mocking.md +0 -59
- package/packages/agent-skills/assets/tdd/references/tests.md +0 -77
- package/packages/agent-skills/assets/testing/SKILL.md +0 -54
- package/packages/agent-skills/assets/testing/references/e2e.md +0 -37
- package/packages/agent-skills/assets/testing/references/integration.md +0 -37
- package/packages/agent-skills/assets/testing/references/unit.md +0 -32
- package/packages/agent-skills/assets/to-feature/SKILL.md +0 -91
- package/packages/agent-skills/assets/to-feature/references/FEATURE-SPEC-TEMPLATE.md +0 -88
- package/packages/agent-skills/assets/to-workitems/SKILL.md +0 -90
- package/packages/agent-skills/assets/to-workitems/references/AZURE-DEVOPS-HIERARCHY.md +0 -121
- package/packages/agent-skills/assets/to-workitems/references/TICKET-TEMPLATES.md +0 -70
- package/packages/azdo/src/workflow-rules-matrix.csv +0 -85
- package/packages/fleet/src/launch-agents.ts +0 -649
- package/packages/repo-controls/assets/GLOBAL-INVARIANTS.md +0 -21
- package/packages/repo-controls/assets/enable-integration-auto-complete.mjs +0 -79
- package/packages/repo-controls/assets/scaffold/docs/notes.md +0 -20
- package/packages/repo-controls/assets/scaffold/plans/GAPS-AND-FINDINGS.md +0 -12
- package/packages/repo-controls/azure-pipelines.yml +0 -274
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/closed_set/fixtures/docs/notes.md +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/english_only/fixtures/excluded/frontend/mobile/src/i18n/locales/zh-CN/strings.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-clean/docs/notes.md +0 -17
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/okf_profile/fixtures/okf-ledger-no-lines/docs/notes.md +0 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-delivery.md +0 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/stable-recommendation.md +0 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/docs/semantics/fixtures/semantics-violations/docs/architecture/product/target-narration.md +0 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-clean/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/a.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-empty-register/frontend/packages/desktop/src/b.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/fresh/fresh.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub1/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub10/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub11/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub2/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub3/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub4/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub5/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub6/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub7/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub8/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/many/sub9/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/services/i18n/locales/de/common.json +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-excluded/frontend/packages/desktop/src/vendored-crate/Cargo.toml +0 -2
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-missing-tree/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/a.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/b.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-no-ceilings/frontend/packages/desktop/src/c.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-stale-entry/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/fresh/fresh.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub1/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub10/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub11/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub2/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub3/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub4/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub5/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub6/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub7/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub8/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/many/sub9/only.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unbaselined/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/directory_ratchet/fixtures/directory-ratchet-unusable-register/frontend/packages/desktop/src/renderer/solo/item.ts +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/GAPS-AND-FINDINGS.md +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/open_work_cap/fixtures/open-work-cap-clean/plans/features/az601-clean/spec.md +0 -10
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/agent/crates/enact-providers/src/snapshots/README.md +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-app/assets/builtin-assistants/avatars/assistant.jpg +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-assets/assets/logos/tools/coding/tool.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/backend/crates/enact-session/src/backend/codex_init_prompt.md +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/mobile/assets/images/icon.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/packages/desktop/src/renderer/assets/logos/brand/app.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/packages/desktop/src/renderer/assets/themes/default-theme.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/public/pwa/icon-192.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-allowed/frontend/resources/product.png +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/placement/fixtures/placement-stray/backend/crates/enact-session/src/backend/README.md +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-clean/plans/features/az347-test-coverage/spec.md +0 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/plans_layout/fixtures/plans-layout-violation/plans/features/az347-test-coverage/spec.md +0 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/__init__.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/check.py +0 -62
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/NotAMigration.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/0001_long_prefix.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/001_init.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/002_add_index.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/003-dashed-slug.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/004_MixedCase.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/005_trailing space.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/01_short_prefix.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/fixtures/migration-filenames/backend/crates/demo/migrations/not-a-migration.sql +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/profile_data/test_check.py +0 -116
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/links.md +0 -20
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-broken/docs/architecture/product/route-target.md +0 -8
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/links.md +0 -20
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/references/fixtures/link-targets-clean/docs/architecture/product/route-target.md +0 -8
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_closed_set/fixtures/.changeset/declared.txt +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/layout/root_markdown_allowlist/fixtures/root-markdown-clean/INDEPENDENT-VERIFICATION.md +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/.gitattributes +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/attribute-without-conversion/large file.bin +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/.gitattributes +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/large file.bin +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/clean/small.txt +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/missing-filter/large file.bin +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/.gitattributes +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/lfs/integrity/fixtures/unmatched-extension/payload.dat +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/__init__.py +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/__init__.py +0 -12
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/check.py +0 -129
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/disjoint/gate-registry.yaml +0 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/empty/gate-registry.yaml +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/fixtures/overlapping/gate-registry.yaml +0 -13
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/pipeline/phase_disjoint/test_check.py +0 -109
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/__init__.py +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/__init__.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/check.py +0 -149
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/clean/docs/readme.md +0 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/fixtures/relevant/VERSION +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/changeset_plan/test_check.py +0 -151
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/__init__.py +0 -11
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/check.py +0 -202
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/changesets/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/.workflows/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/CHANGELOG.md +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/aligned/VERSION +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/changesets/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/.workflows/package.json +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/CHANGELOG.md +0 -5
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/fixtures/mismatched/VERSION +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/release/version_release/test_check.py +0 -162
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/banned_commands/check.py +0 -103
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.lock +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/Cargo.toml +0 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/rust-toolchain.toml +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/backend/src/main.rs +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/clean/frontend/bun.lock +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.lock +0 -7
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/Cargo.toml +0 -4
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/rust-toolchain.toml +0 -3
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/backend/src/main.rs +0 -1
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/checks/repository/lockfile_integrity/fixtures/empty-bun-lock/frontend/bun.lock +0 -0
- package/packages/repo-controls/package/adl-compliance/src/adl_compliance/policy/grants-registry.yaml +0 -112
- package/packages/repo-controls/workflows/scripts/release/verify-npm-artifact.ts +0 -119
|
@@ -42,6 +42,16 @@ export interface DeploySkillsOptions {
|
|
|
42
42
|
readonly profile?: DeployProfile;
|
|
43
43
|
/** Computes the exact same written/unchanged/removed/conflicts/retiredRemovals result without writing anything (independent review, 2026-09-29, finding 6). */
|
|
44
44
|
readonly dryRun?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* When set, deploy only the named surface (profile.surfaces[].name) —
|
|
47
|
+
* the deploy half of the per-surface knob (az567). Other surfaces are
|
|
48
|
+
* left untouched: their files are not written, their conflicts are not
|
|
49
|
+
* reported, their retired-folder cleanup does not run. The deploy
|
|
50
|
+
* profile's `retired` list is the closed set of "skill folders no
|
|
51
|
+
* surface reads any more"; for surface-prepare this list is also
|
|
52
|
+
* filtered to the active surfaces.
|
|
53
|
+
*/
|
|
54
|
+
readonly surfaceFilter?: string;
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export interface SkillConflict {
|
|
@@ -95,6 +105,201 @@ export function removeEmptyParents(root: string, dir: string): void {
|
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
107
|
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// az696 — `adl skills uninstall`: the removal primitive.
|
|
110
|
+
//
|
|
111
|
+
// `removeDeployedSkillFolders` removes the skill folders a previous
|
|
112
|
+
// `deploySkills` wrote, in the same resolution order (assets/ as the
|
|
113
|
+
// source of truth, the deploy profile as the closed set of surfaces, the
|
|
114
|
+
// repo's `[assets.skills]` subset respected on input).
|
|
115
|
+
//
|
|
116
|
+
// The function is split out from `deploySkills` because the deployment
|
|
117
|
+
// hot path should never carry the removal logic. The two are
|
|
118
|
+
// intentionally NOT one function with a flag — deploy's return value
|
|
119
|
+
// (written/unchanged/removed/conflicts/adopted) and uninstall's return
|
|
120
|
+
// value (removed/absent/refused) carry different fields, and a single
|
|
121
|
+
// result type would force both callers to ignore the half they don't
|
|
122
|
+
// care about.
|
|
123
|
+
//
|
|
124
|
+
// Safety: the only paths this function will remove are exactly the paths
|
|
125
|
+
// recorded in the deploy manifest. Anything else in the target repo —
|
|
126
|
+
// a hand-authored skill, an extra file the manifest did not write, a
|
|
127
|
+
// hand-edited deploy-written file — is reported in `refused` and never
|
|
128
|
+
// touched. This mirrors deploy's own contract that it never writes a
|
|
129
|
+
// file it did not itself originate (deploy.ts's header).
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
export interface SkillRefusal {
|
|
133
|
+
readonly folder: string;
|
|
134
|
+
readonly skill: string;
|
|
135
|
+
/** Repo-relative posix path to the entry the primitive refused to remove. */
|
|
136
|
+
readonly path: string;
|
|
137
|
+
/** Human-readable reason the entry was refused. */
|
|
138
|
+
readonly reason: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface RemoveDeployedSkillFoldersOptions {
|
|
142
|
+
readonly assetsDir: string;
|
|
143
|
+
readonly targetRepo: string;
|
|
144
|
+
/** Defaults to loadProfile() (the package's own deploy-profile.json) — pass explicitly when the caller has already resolved real on-disk paths (see profile.ts's header). */
|
|
145
|
+
readonly profile?: DeployProfile;
|
|
146
|
+
/** When set, only the skill folders for this surface (profile.surfaces[].name) are in scope. */
|
|
147
|
+
readonly surfaceFilter?: string;
|
|
148
|
+
/** When set, only this skill id (assets/ skill folder name) is in scope. */
|
|
149
|
+
readonly skillFilter?: string;
|
|
150
|
+
/** Computes the exact same removed/absent/refused result without unlinking anything. */
|
|
151
|
+
readonly dryRun?: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface RemoveDeployedSkillFoldersResult {
|
|
155
|
+
/** Repo-relative posix paths the primitive actually removed (or would have, in --dry-run). Sorted, deduplicated. */
|
|
156
|
+
readonly removed: readonly string[];
|
|
157
|
+
/** (surface, skill) pairs the deploy profile resolves to that are in scope but were already absent on disk. Sorted, deduplicated, "<folder>/<skill>". */
|
|
158
|
+
readonly absent: readonly string[];
|
|
159
|
+
/** Entries the manifest did not record but which live inside a managed skill dir or surface folder — never removed, always reported. */
|
|
160
|
+
readonly refused: readonly SkillRefusal[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The deployed-skill removal primitive (az696). Removes every file the
|
|
165
|
+
* deploy manifest records, restricted by `surfaceFilter`/`skillFilter`
|
|
166
|
+
* (intersection) and the repo's `[assets.skills]` subset. Idempotent: a
|
|
167
|
+
* second call with the same arguments reports every target in `absent`.
|
|
168
|
+
*/
|
|
169
|
+
export function removeDeployedSkillFolders(options: RemoveDeployedSkillFoldersOptions): RemoveDeployedSkillFoldersResult {
|
|
170
|
+
const { assetsDir, targetRepo } = options;
|
|
171
|
+
const dryRun = options.dryRun === true;
|
|
172
|
+
|
|
173
|
+
if (!existsSync(assetsDir) || !statSync(assetsDir).isDirectory()) {
|
|
174
|
+
throw new Error(`agent-skills assets directory not found: ${assetsDir}`);
|
|
175
|
+
}
|
|
176
|
+
if (!existsSync(targetRepo) || !statSync(targetRepo).isDirectory()) {
|
|
177
|
+
throw new Error(`uninstall target is not a directory: ${targetRepo}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const profile = options.profile ?? loadProfile();
|
|
181
|
+
const availableSkills = listSourceSkillNames(assetsDir);
|
|
182
|
+
|
|
183
|
+
const selection = resolveSkillSelection(targetRepo, availableSkills);
|
|
184
|
+
if (!selection.ok) throw new Error(selection.error);
|
|
185
|
+
const selectedSkills = [...new Set(selection.skills)].sort();
|
|
186
|
+
|
|
187
|
+
const manifest = readManifest(targetRepo);
|
|
188
|
+
|
|
189
|
+
// Build the in-scope (surface, skill) set, applying both filters as
|
|
190
|
+
// intersection. An unknown surfaceFilter or skillFilter is silently
|
|
191
|
+
// out-of-scope — the caller (the CLI) validates the names first and
|
|
192
|
+
// the primitive trusts its inputs.
|
|
193
|
+
const inScopeSurfaces = profile.surfaces.filter(
|
|
194
|
+
(surface) => options.surfaceFilter === undefined || surface.name === options.surfaceFilter,
|
|
195
|
+
);
|
|
196
|
+
const inScopeSkills = selectedSkills.filter(
|
|
197
|
+
(skill) => options.skillFilter === undefined || skill === options.skillFilter,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// Build the manifest-ownership set restricted to the in-scope
|
|
201
|
+
// (surface, skill) pairs. Everything the manifest records under those
|
|
202
|
+
// pairs is removable; everything else is not in scope.
|
|
203
|
+
const ownedPaths = new Set<string>();
|
|
204
|
+
for (const surface of inScopeSurfaces) {
|
|
205
|
+
for (const skill of inScopeSkills) {
|
|
206
|
+
const prefix = `${surface.folder}/${skill}/`;
|
|
207
|
+
for (const file of manifest.files) {
|
|
208
|
+
if (file.startsWith(prefix)) ownedPaths.add(file);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const removed: string[] = [];
|
|
214
|
+
for (const relPath of [...ownedPaths].sort()) {
|
|
215
|
+
const absolute = join(targetRepo, relPath);
|
|
216
|
+
if (existsSync(absolute)) {
|
|
217
|
+
if (!dryRun) {
|
|
218
|
+
unlinkSync(absolute);
|
|
219
|
+
removeEmptyParents(targetRepo, dirname(absolute));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
removed.push(relPath);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Safety check: every path inside a managed skill dir or surface
|
|
226
|
+
// folder that is NOT in the manifest is reported as a refusal. The
|
|
227
|
+
// surface folder itself (e.g. `.claude/skills`) is reported when it
|
|
228
|
+
// exists and is not under any managed skill dir — the agent never
|
|
229
|
+
// removes the surface root on its own, only the skill dirs under it.
|
|
230
|
+
const refused: SkillRefusal[] = [];
|
|
231
|
+
for (const surface of inScopeSurfaces) {
|
|
232
|
+
const surfaceAbs = join(targetRepo, surface.folder);
|
|
233
|
+
if (!existsSync(surfaceAbs)) continue;
|
|
234
|
+
const inScope = options.skillFilter === undefined
|
|
235
|
+
? inScopeSkills
|
|
236
|
+
: inScopeSkills.filter((skill) => skill === options.skillFilter);
|
|
237
|
+
|
|
238
|
+
// Hand-authored skill dirs the manifest did not write.
|
|
239
|
+
for (const name of directoryNames(surfaceAbs)) {
|
|
240
|
+
if (inScope.includes(name)) continue;
|
|
241
|
+
// A skill the manifest owns under this surface is in-scope; an
|
|
242
|
+
// unrecorded same-name dir is user-authored and refused.
|
|
243
|
+
const isManaged = manifest.files.some((file) => file.startsWith(`${surface.folder}/${name}/`));
|
|
244
|
+
if (isManaged) continue; // already handled in the removal pass above
|
|
245
|
+
refused.push({
|
|
246
|
+
folder: surface.folder,
|
|
247
|
+
skill: name,
|
|
248
|
+
path: `${surface.folder}/${name}`,
|
|
249
|
+
reason: "folder is not recorded in the deploy manifest; refusing to remove user-authored content",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// For each in-scope skill dir, any file inside that is not in the
|
|
254
|
+
// manifest is reported and never removed.
|
|
255
|
+
for (const skill of inScope) {
|
|
256
|
+
const skillDirAbs = join(surfaceAbs, skill);
|
|
257
|
+
if (!existsSync(skillDirAbs)) continue;
|
|
258
|
+
const isManaged = manifest.files.some((file) => file.startsWith(`${surface.folder}/${skill}/`));
|
|
259
|
+
if (!isManaged) continue; // already refused above as user-authored
|
|
260
|
+
for (const relFile of walkFiles(skillDirAbs)) {
|
|
261
|
+
const relPath = `${surface.folder}/${skill}/${relFile}`;
|
|
262
|
+
if (ownedPaths.has(relPath)) continue;
|
|
263
|
+
refused.push({
|
|
264
|
+
folder: surface.folder,
|
|
265
|
+
skill,
|
|
266
|
+
path: relPath,
|
|
267
|
+
reason: "file is inside a managed skill dir but is not recorded in the deploy manifest; refusing to remove user-authored content",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
refused.sort((a, b) => a.path.localeCompare(b.path));
|
|
273
|
+
|
|
274
|
+
// The "absent" report: every (surface, skill) pair the deploy profile
|
|
275
|
+
// resolves to, filtered to in-scope, that has no files on disk and no
|
|
276
|
+
// manifest record. This is the idempotency signal a second run will
|
|
277
|
+
// see.
|
|
278
|
+
const absent: string[] = [];
|
|
279
|
+
for (const surface of inScopeSurfaces) {
|
|
280
|
+
for (const skill of inScopeSkills) {
|
|
281
|
+
const skillDirAbs = join(targetRepo, surface.folder, skill);
|
|
282
|
+
const prefix = `${surface.folder}/${skill}/`;
|
|
283
|
+
const hasManifestEntry = manifest.files.some((file) => file.startsWith(prefix));
|
|
284
|
+
if (!hasManifestEntry && !existsSync(skillDirAbs)) {
|
|
285
|
+
absent.push(`${surface.folder}/${skill}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
absent.sort();
|
|
290
|
+
|
|
291
|
+
// Rewrite the manifest to drop the paths we removed (and only those).
|
|
292
|
+
// The surviving set keeps every path the manifest recorded that is
|
|
293
|
+
// outside the in-scope (surface, skill) pairs — those belong to other
|
|
294
|
+
// surfaces/skills the user did not ask to uninstall.
|
|
295
|
+
if (!dryRun) {
|
|
296
|
+
const surviving = manifest.files.filter((file) => !ownedPaths.has(file));
|
|
297
|
+
writeManifest(targetRepo, surviving);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return { removed: removed.sort(), absent, refused };
|
|
301
|
+
}
|
|
302
|
+
|
|
98
303
|
export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
99
304
|
const { assetsDir, targetRepo } = options;
|
|
100
305
|
const dryRun = options.dryRun === true;
|
|
@@ -164,6 +369,10 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
164
369
|
}
|
|
165
370
|
};
|
|
166
371
|
for (const surface of profile.surfaces) {
|
|
372
|
+
// az567 surfaceFilter: skip non-target surfaces entirely. The
|
|
373
|
+
// legacy-takeover pass for the non-target surface is irrelevant
|
|
374
|
+
// when only one surface is being deployed.
|
|
375
|
+
if (options.surfaceFilter !== undefined && surface.name !== options.surfaceFilter) continue;
|
|
167
376
|
for (const skill of selectedSkills) {
|
|
168
377
|
const skillDirRel = `${surface.folder}/${skill}`;
|
|
169
378
|
const skillDirAbs = join(targetRepo, skillDirRel);
|
|
@@ -180,12 +389,23 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
180
389
|
|
|
181
390
|
legacyRetired(surface.folder, true);
|
|
182
391
|
}
|
|
183
|
-
for (const folder of retiredFolders(profile))
|
|
392
|
+
for (const folder of retiredFolders(profile)) {
|
|
393
|
+
// az567 surfaceFilter: retired-folder cleanup is also filtered to
|
|
394
|
+
// the active surfaces. A non-target surface's retired folder is
|
|
395
|
+
// left exactly as found.
|
|
396
|
+
if (options.surfaceFilter !== undefined) {
|
|
397
|
+
const isTargetActive = profile.surfaces.some((s) => s.folder === folder && s.name === options.surfaceFilter);
|
|
398
|
+
if (!isTargetActive) continue;
|
|
399
|
+
}
|
|
400
|
+
legacyRetired(folder, false);
|
|
401
|
+
}
|
|
184
402
|
}
|
|
185
403
|
|
|
186
404
|
const manifestFiles = new Set([...manifest.files, ...takeoverFiles]);
|
|
187
405
|
const adopted: string[] = [];
|
|
188
406
|
|
|
407
|
+
// Total over `selectedSkills`: every loop below iterates that same array, so
|
|
408
|
+
// `sourceFilesBySkill.get(skill)` is never undefined and needs no fallback.
|
|
189
409
|
const sourceFilesBySkill = new Map<string, readonly string[]>();
|
|
190
410
|
for (const skill of selectedSkills) sourceFilesBySkill.set(skill, walkFiles(join(assetsDir, skill)));
|
|
191
411
|
|
|
@@ -195,6 +415,10 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
195
415
|
const keptPaths = new Set<string>();
|
|
196
416
|
|
|
197
417
|
for (const surface of profile.surfaces) {
|
|
418
|
+
// az567 surfaceFilter: only iterate the named surface. Other
|
|
419
|
+
// surfaces' deploy paths, conflicts, and removal decisions are out
|
|
420
|
+
// of scope and never appear in this result.
|
|
421
|
+
if (options.surfaceFilter !== undefined && surface.name !== options.surfaceFilter) continue;
|
|
198
422
|
for (const skill of selectedSkills) {
|
|
199
423
|
const skillDirRel = `${surface.folder}/${skill}`;
|
|
200
424
|
const skillDirAbs = join(targetRepo, skillDirRel);
|
|
@@ -204,7 +428,7 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
204
428
|
// An unrecorded folder whose whole tree is byte-identical to the asset
|
|
205
429
|
// is adopted: its files fall through to the write loop below as
|
|
206
430
|
// unchanged and so land in the manifest. Anything else stays a conflict.
|
|
207
|
-
if (skillDirExists && !managedByUs && folderIdenticalToAsset(skillDirAbs, join(assetsDir, skill), sourceFilesBySkill.get(skill)
|
|
431
|
+
if (skillDirExists && !managedByUs && folderIdenticalToAsset(skillDirAbs, join(assetsDir, skill), sourceFilesBySkill.get(skill)!)) {
|
|
208
432
|
managedByUs = true;
|
|
209
433
|
adopted.push(skillDirRel);
|
|
210
434
|
}
|
|
@@ -220,7 +444,7 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
220
444
|
continue; // never write into it, never claim its files in the manifest
|
|
221
445
|
}
|
|
222
446
|
|
|
223
|
-
for (const relFile of sourceFilesBySkill.get(skill)
|
|
447
|
+
for (const relFile of sourceFilesBySkill.get(skill)!) {
|
|
224
448
|
const targetRel = `${skillDirRel}/${relFile}`;
|
|
225
449
|
keptPaths.add(targetRel);
|
|
226
450
|
const targetAbs = join(targetRepo, targetRel);
|
|
@@ -245,6 +469,14 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
245
469
|
// profile actually manages, even though every manifest entry should
|
|
246
470
|
// already satisfy that by construction.
|
|
247
471
|
if (!profile.surfaces.some((surface) => relPath.startsWith(`${surface.folder}/`))) continue;
|
|
472
|
+
// az567 surfaceFilter: paths under a non-target surface are not in
|
|
473
|
+
// scope and must not be removed during a per-surface deploy.
|
|
474
|
+
if (options.surfaceFilter !== undefined) {
|
|
475
|
+
const inTargetSurface = profile.surfaces.some(
|
|
476
|
+
(surface) => surface.name === options.surfaceFilter && relPath.startsWith(`${surface.folder}/`),
|
|
477
|
+
);
|
|
478
|
+
if (!inTargetSurface) continue;
|
|
479
|
+
}
|
|
248
480
|
const absolute = join(targetRepo, relPath);
|
|
249
481
|
if (existsSync(absolute)) {
|
|
250
482
|
if (!dryRun) {
|
|
@@ -270,6 +502,12 @@ export function deploySkills(options: DeploySkillsOptions): DeploySkillsResult {
|
|
|
270
502
|
const retiredRemovals: string[] = [];
|
|
271
503
|
const availableSkillSet = new Set(availableSkills);
|
|
272
504
|
for (const folder of retiredFolders(profile)) {
|
|
505
|
+
// az567 surfaceFilter: only clean up retired folders that are NOT
|
|
506
|
+
// tied to a non-target surface. A retired folder is per-profile
|
|
507
|
+
// (the active-surface filters above do not reduce the retired list);
|
|
508
|
+
// when a surfaceFilter is set, retired-folder cleanup runs the same
|
|
509
|
+
// way it does for a full deploy, because retired folders are not
|
|
510
|
+
// tied to a particular active surface.
|
|
273
511
|
const folderAbs = join(targetRepo, folder);
|
|
274
512
|
if (!existsSync(folderAbs)) continue;
|
|
275
513
|
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
deploySkills,
|
|
3
|
+
type DeploySkillsOptions,
|
|
4
|
+
type DeploySkillsResult,
|
|
5
|
+
type SkillConflict,
|
|
6
|
+
removeDeployedSkillFolders,
|
|
7
|
+
type RemoveDeployedSkillFoldersOptions,
|
|
8
|
+
type RemoveDeployedSkillFoldersResult,
|
|
9
|
+
type SkillRefusal,
|
|
10
|
+
} from "./deploy.js";
|
|
2
11
|
export {
|
|
3
12
|
doctorAssets,
|
|
4
13
|
doctorDeployment,
|
package/packages/azdo/README.md
CHANGED
|
@@ -68,13 +68,22 @@ adl mcp cli pr-create-or-update --action create \
|
|
|
68
68
|
--title 'feat(cli): add direct mode' \
|
|
69
69
|
--description $'## Summary\n\nAdds a CLI path.\n\n## Why\n\nMCP can be unavailable.\n\n## Changes\n\n- Add CLI mode'
|
|
70
70
|
adl mcp cli build-run --id 17 --branch refs/heads/integration
|
|
71
|
-
adl mcp cli workitem-create-or-update --
|
|
71
|
+
adl mcp cli workitem-create-or-update-bug --title 'CLI failure' --parent-id 378 --effort 1 --description $'## Current behaviour\n\nCLI exits 2.\n\n## Expected behaviour\n\nExits 0.\n\n## Behaviour that must not change\n\nJSON on stdout.\n\n## Responsible Feature\n\naz378\n\n## Verification\n\nRe-run the failing command.'
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Run `adl mcp cli --help` for the complete flag contract. The CLI has exactly the
|
|
75
|
-
current MCP surface: `pr_list_or_get`, `pr_create_or_update`, `
|
|
76
|
-
`build_run`, `workitem_list_or_get`,
|
|
77
|
-
|
|
75
|
+
current MCP surface: `pr_list_or_get`, `pr_create_or_update`, `pr_review`,
|
|
76
|
+
`build_list_or_get`, `build_run`, `workitem_list_or_get`,
|
|
77
|
+
`workitem_create_or_update_feature`, `workitem_create_or_update_pbi`,
|
|
78
|
+
`workitem_create_or_update_bug`, `workitem_comments_read`, `workitem_attachments`,
|
|
79
|
+
`activity_add` and `activity_list`; the CLI exposes their hyphenated equivalents.
|
|
80
|
+
The blanket `workitem_create_or_update` was split per work-item type so the agent
|
|
81
|
+
gate can enforce each type's mandatory fields (Feature: title + parentId + priority
|
|
82
|
+
+ intent.md description; PBI/Bug: title + parentId + item-file description + effort
|
|
83
|
+
in days; `comment` on every update of any type); the read side
|
|
84
|
+
(`workitem_list_or_get`) stays one tool. Per-type mandatory fields and the
|
|
85
|
+
heading-parse gates: see
|
|
86
|
+
`packages/agent-skills/assets/workitems-process/references/ado-and-git.md` §1a.
|
|
78
87
|
|
|
79
88
|
## Source
|
|
80
89
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bun test config — like Python's conftest.py: a preload file runs once
|
|
2
|
+
# before every test in this package. tests/setup.ts installs the test-only
|
|
3
|
+
# AzureRemote defaults (org / project / repository) that the resolvers
|
|
4
|
+
# fall back to when no argument and no git remote resolve.
|
|
5
|
+
[test]
|
|
6
|
+
preload = ["./tests/setup.ts"]
|
|
@@ -63,7 +63,8 @@ export async function uploadAttachment(c: AdoClient, workItemId: number, fileNam
|
|
|
63
63
|
/** The T2 guard (board-rules.md § Attachments): a plan-r* file refuses deletion once any item of its Feature has left Awaiting Approval — the plan G1 approved stays on the board. */
|
|
64
64
|
async function assertPlanAttachmentDeletable(c: AdoClient, feature: any, fileName: string): Promise<void> {
|
|
65
65
|
const relationId = (url: string) => Number(/(\d+)\/?$/.exec(url)?.[1]);
|
|
66
|
-
|
|
66
|
+
// `feature.relations` is always an array here: attachedFiles() returned a target off this same object, so the property was a non-empty array moments earlier -- the nullish fallback was unreachable.
|
|
67
|
+
const childIds = feature.relations
|
|
67
68
|
.filter((relation: any) => relation.rel === 'System.LinkTypes.Hierarchy-Forward')
|
|
68
69
|
.map((relation: any) => relationId(relation.url))
|
|
69
70
|
.filter((id: number) => Number.isInteger(id));
|
|
@@ -22,8 +22,8 @@ export function resetClientForTests(): void {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** The human-facing PR link, built from the same org/project root the API call uses. */
|
|
25
|
-
export function pullRequestWebUrl(repository: string, pullRequestId: number | string): string {
|
|
26
|
-
return `${adoProjectUrl()}/_git/${repository}/pullrequest/${pullRequestId}`;
|
|
25
|
+
export function pullRequestWebUrl(repository: string, pullRequestId: number | string, cwd?: string): string {
|
|
26
|
+
return `${adoProjectUrl(cwd)}/_git/${repository}/pullrequest/${pullRequestId}`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -127,6 +127,20 @@ export async function prListOrGet(args: PrListOrGetArgs): Promise<string> {
|
|
|
127
127
|
pullRequestsPath(repository, args.id),
|
|
128
128
|
{ 'api-version': '7.1' }
|
|
129
129
|
);
|
|
130
|
+
// Embed the relation inline (Bug 706) so callers can verify the
|
|
131
|
+
// work-items set on the single-PR view without a second request. The
|
|
132
|
+
// PR's own response carries `_links.workItems.href` but no inline
|
|
133
|
+
// array; this follow-on reads the sub-resource and overlays the ids.
|
|
134
|
+
// The list branch below intentionally keeps the small PR shape
|
|
135
|
+
// (no inline array); only the per-PR fetch path embeds it.
|
|
136
|
+
const workItems = await c.get<{ value?: Array<{ id?: number }> }>(
|
|
137
|
+
pullRequestsPath(repository, args.id) + '/workItems',
|
|
138
|
+
{ 'api-version': '7.1' }
|
|
139
|
+
);
|
|
140
|
+
pr.workItems = (workItems.value ?? [])
|
|
141
|
+
.map((entry) => Number(entry.id))
|
|
142
|
+
.filter((id) => Number.isInteger(id))
|
|
143
|
+
.sort((a, b) => a - b);
|
|
130
144
|
return JSON.stringify(pr, null, 2);
|
|
131
145
|
}
|
|
132
146
|
|
|
@@ -180,6 +194,10 @@ export type PrCreateOrUpdateArgs = {
|
|
|
180
194
|
deleteSourceBranch?: boolean;
|
|
181
195
|
/** P4: the content of the kind's mandatory Feature attachment (planning/implementation PRs only). */
|
|
182
196
|
attachmentContent?: string;
|
|
197
|
+
/** P5: positive integer work-item ids to add to the PR's work-items relation on update. */
|
|
198
|
+
addWorkItems?: number[];
|
|
199
|
+
/** P5: positive integer work-item ids to remove from the PR's work-items relation on update. */
|
|
200
|
+
removeWorkItems?: number[];
|
|
183
201
|
/** Overrides `AZURE_DEVOPS_REPO` for this call. */
|
|
184
202
|
repository?: string;
|
|
185
203
|
/** Rejected removed routing input; PRs always use the checked-out branch. */
|
|
@@ -188,11 +206,146 @@ export type PrCreateOrUpdateArgs = {
|
|
|
188
206
|
targetBranch?: never;
|
|
189
207
|
};
|
|
190
208
|
|
|
209
|
+
/**
|
|
210
|
+
* P5: the public signature of the post-create work-items update.
|
|
211
|
+
*
|
|
212
|
+
* The shape per the doc the human pasted (2026-10-14): a separate
|
|
213
|
+
* `/workitems` sub-endpoint does not exist; the PR's own endpoint does
|
|
214
|
+
* not honour `workItems` as an updatable field. The real route is the
|
|
215
|
+
* work-item's `relations` array, mutated via JSON-Patch on the work item
|
|
216
|
+
* itself. Each `--add-work-items` id gets an `ArtifactLink` of rel
|
|
217
|
+
* `Pull Request` pointing at the PR; the PR's `workItems` sub-endpoint
|
|
218
|
+
* (a READ) then lists the same ids, which is the live verification.
|
|
219
|
+
*
|
|
220
|
+
* PATCH /_apis/wit/workitems/{workItemId}?api-version=7.1
|
|
221
|
+
* Content-Type: application/json-patch+json
|
|
222
|
+
* Body: [{ op: "add", path: "/relations/-", value: { rel: "ArtifactLink",
|
|
223
|
+
* url: "vstfs:///Git/PullRequestId/{projectId}%2F{repoId}%2F{prId}",
|
|
224
|
+
* attributes: { name: "Pull Request" } } }]
|
|
225
|
+
*
|
|
226
|
+
* The function validates the inputs (no id in both lists; positive integers;
|
|
227
|
+
* dedupe), and for each add issues a PATCH on the work item. A `remove`
|
|
228
|
+
* issues a JSON-Patch `remove` on the matching `ArtifactLink` (matched by
|
|
229
|
+
* the work item's `relations` array; the `url` field is the only stable
|
|
230
|
+
* identifier). Returns the merged id set the PR's workItems sub-endpoint
|
|
231
|
+
* reports back. The `pr_create_or_update` tool's update path calls this
|
|
232
|
+
* when at least one of `--add-work-items` / `--remove-work-items` is
|
|
233
|
+
* supplied.
|
|
234
|
+
*/
|
|
235
|
+
export type UpdatePrWorkItemLinksArgs = {
|
|
236
|
+
add: number[];
|
|
237
|
+
remove: number[];
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export async function updatePrWorkItemLinks(
|
|
241
|
+
c: AdoClient,
|
|
242
|
+
prId: number,
|
|
243
|
+
args: UpdatePrWorkItemLinksArgs,
|
|
244
|
+
): Promise<number[]> {
|
|
245
|
+
const additions = dedupeIntegers(args.add);
|
|
246
|
+
const removals = dedupeIntegers(args.remove);
|
|
247
|
+
const overlap = additions.find((id) => removals.includes(id));
|
|
248
|
+
if (overlap !== undefined) {
|
|
249
|
+
throw new Error(`work-item ${overlap} is in both add and remove; an id can only appear on one side of the PATCH (P5 FENCE)`);
|
|
250
|
+
}
|
|
251
|
+
if (!Number.isInteger(prId) || prId <= 0) {
|
|
252
|
+
throw new Error(`prId must be a positive integer`);
|
|
253
|
+
}
|
|
254
|
+
// Resolve the project + repository GUIDs at call time from ADO. The vstfs
|
|
255
|
+
// URL is `vstfs:///Git/PullRequestId/{projectId}%2F{repoId}%2F{prId}` —
|
|
256
|
+
// both GUIDs are required and are not on the PR's own response. The
|
|
257
|
+
// canonical Azure DevOps REST shape returns both on
|
|
258
|
+
// `GET /{org}/{project}/_apis/git/repositories/{repoName}`. We cache the
|
|
259
|
+
// resolution so the loop below does not call it per work item.
|
|
260
|
+
const repository = resolveRepository();
|
|
261
|
+
const repoDoc = await c.get<{ id?: string; project?: { id?: string } }>(
|
|
262
|
+
`/git/repositories/${repository}`,
|
|
263
|
+
{ 'api-version': '7.1' },
|
|
264
|
+
);
|
|
265
|
+
const repoId = repoDoc.id;
|
|
266
|
+
const projectId = repoDoc.project?.id;
|
|
267
|
+
if (!repoId || !projectId) {
|
|
268
|
+
throw new Error(`updatePrWorkItemLinks could not resolve repository GUIDs; GET .../repositories/${repository} returned id=${repoId}, project.id=${projectId}`);
|
|
269
|
+
}
|
|
270
|
+
const prLinkUrl = `vstfs:///Git/PullRequestId/${projectId}%2F${repoId}%2F${prId}`;
|
|
271
|
+
for (const workItemId of additions) {
|
|
272
|
+
const current = await c.get<{ relations?: Array<{ rel?: string; url?: string }> }>(
|
|
273
|
+
`/wit/workitems/${workItemId}`,
|
|
274
|
+
{ 'api-version': '7.1', $expand: 'relations' },
|
|
275
|
+
);
|
|
276
|
+
const relations = current.relations ?? [];
|
|
277
|
+
const alreadyLinked = relations.some(
|
|
278
|
+
(rel) => rel.rel === 'ArtifactLink' && rel.url === prLinkUrl,
|
|
279
|
+
);
|
|
280
|
+
if (alreadyLinked) continue;
|
|
281
|
+
await c.patch<unknown>(
|
|
282
|
+
`/wit/workitems/${workItemId}`,
|
|
283
|
+
[
|
|
284
|
+
{
|
|
285
|
+
op: 'add',
|
|
286
|
+
path: '/relations/-',
|
|
287
|
+
value: {
|
|
288
|
+
rel: 'ArtifactLink',
|
|
289
|
+
url: prLinkUrl,
|
|
290
|
+
attributes: { name: 'Pull Request' },
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
{ 'api-version': '7.1' },
|
|
295
|
+
'application/json-patch+json',
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
if (removals.length > 0) {
|
|
299
|
+
// For each work item, fetch the current relations, find the one whose
|
|
300
|
+
// url matches the PR's vstfs URL, and remove it via JSON-Patch.
|
|
301
|
+
for (const workItemId of removals) {
|
|
302
|
+
const current = await c.get<{ relations?: Array<{ rel?: string; url?: string }> }>(
|
|
303
|
+
`/wit/workitems/${workItemId}`,
|
|
304
|
+
{ 'api-version': '7.1', $expand: 'relations' },
|
|
305
|
+
);
|
|
306
|
+
const relations = current.relations ?? [];
|
|
307
|
+
const targetIndex = relations.findIndex((r) => r.url === prLinkUrl);
|
|
308
|
+
if (targetIndex === -1) continue;
|
|
309
|
+
await c.patch<unknown>(
|
|
310
|
+
`/wit/workitems/${workItemId}`,
|
|
311
|
+
[{ op: 'remove', path: `/relations/${targetIndex}` }],
|
|
312
|
+
{ 'api-version': '7.1' },
|
|
313
|
+
'application/json-patch+json',
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Read the PR's workItems sub-endpoint as the verification surface. This
|
|
318
|
+
// is the only server endpoint that lists the relation; the PR's own
|
|
319
|
+
// response does not carry workItems. The path uses the repository NAME
|
|
320
|
+
// (the package convention for REST URLs — see `pullRequestsPath`), not
|
|
321
|
+
// the GUID that the vstfs URL above embeds.
|
|
322
|
+
const verify = await c.get<{ value?: Array<{ id?: number }> }>(
|
|
323
|
+
pullRequestsPath(repository, prId) + '/workItems',
|
|
324
|
+
{ 'api-version': '7.1' },
|
|
325
|
+
);
|
|
326
|
+
return (verify.value ?? []).map((entry) => Number(entry.id)).filter((id) => Number.isInteger(id)).sort((a, b) => a - b);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function dedupeIntegers(list: readonly number[]): number[] {
|
|
330
|
+
const seen = new Set<number>();
|
|
331
|
+
const out: number[] = [];
|
|
332
|
+
for (const value of list) {
|
|
333
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
334
|
+
throw new Error(`work-item ids must be positive integers`);
|
|
335
|
+
}
|
|
336
|
+
if (seen.has(value)) continue;
|
|
337
|
+
seen.add(value);
|
|
338
|
+
out.push(value);
|
|
339
|
+
}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
|
|
191
343
|
export function assertPrSourceBranch(source: string | undefined): asserts source is string {
|
|
192
344
|
if (!source) throw new Error('could not determine the current branch; run from the repository checkout');
|
|
193
345
|
if (source === 'HEAD') throw new Error('this checkout is in detached HEAD state; check out a branch first');
|
|
194
|
-
|
|
195
|
-
if (source ===
|
|
346
|
+
const target = PR_TARGET_BRANCH;
|
|
347
|
+
if (source === target) throw new Error(`already on ${target}; create a feature branch first`);
|
|
348
|
+
if (source === 'main') throw new Error(`a pull request is never opened from main; open a branch into ${target}`);
|
|
196
349
|
}
|
|
197
350
|
|
|
198
351
|
/**
|
|
@@ -344,7 +497,9 @@ export async function prCreateOrUpdate(args: PrCreateOrUpdateArgs): Promise<stri
|
|
|
344
497
|
const repository = resolveRepository(args.repository);
|
|
345
498
|
|
|
346
499
|
if (args.id) {
|
|
347
|
-
const { id, title, description, autoComplete, mergeStrategy, deleteSourceBranch } = args;
|
|
500
|
+
const { id, title, description, autoComplete, mergeStrategy, deleteSourceBranch, addWorkItems, removeWorkItems } = args;
|
|
501
|
+
const prId = Number(id);
|
|
502
|
+
const hasWorkItemChange = (addWorkItems?.length ?? 0) > 0 || (removeWorkItems?.length ?? 0) > 0;
|
|
348
503
|
|
|
349
504
|
assertValidUpdateMetadata(title, description)
|
|
350
505
|
|
|
@@ -370,10 +525,31 @@ export async function prCreateOrUpdate(args: PrCreateOrUpdateArgs): Promise<stri
|
|
|
370
525
|
Object.assign(body, autoCompleteBody(existing.createdBy?.id ?? ''))
|
|
371
526
|
}
|
|
372
527
|
|
|
373
|
-
if (Object.keys(body).length === 0) {
|
|
528
|
+
if (Object.keys(body).length === 0 && !hasWorkItemChange) {
|
|
374
529
|
throw new Error('update requires title, description, or completion options');
|
|
375
530
|
}
|
|
376
531
|
|
|
532
|
+
if (hasWorkItemChange) {
|
|
533
|
+
// P5: post-create mutability of the PR's work-items relation. Runs even
|
|
534
|
+
// when no metadata changes are present, so an ordinary PR whose source
|
|
535
|
+
// branch name does not match any of the three branch-name regexes still
|
|
536
|
+
// has a way to be wired. The flag path replaces workItemRefs at create
|
|
537
|
+
// for ordinary PRs (and complements it for implementation/planning).
|
|
538
|
+
const linked = await updatePrWorkItemLinks(c, prId, {
|
|
539
|
+
add: addWorkItems ?? [],
|
|
540
|
+
remove: removeWorkItems ?? [],
|
|
541
|
+
});
|
|
542
|
+
if (Object.keys(body).length === 0) {
|
|
543
|
+
return JSON.stringify({ pullRequestId: prId, workItems: linked, summary: 'work-items relation updated; no PR metadata changed' }, null, 2);
|
|
544
|
+
}
|
|
545
|
+
const pr = await c.patch<any>(
|
|
546
|
+
pullRequestsPath(repository, id),
|
|
547
|
+
body,
|
|
548
|
+
{ 'api-version': '7.1' }
|
|
549
|
+
);
|
|
550
|
+
return JSON.stringify({ ...pr, workItems: linked, summary: 'PR metadata and work-items relation updated' }, null, 2);
|
|
551
|
+
}
|
|
552
|
+
|
|
377
553
|
const pr = await c.patch<any>(
|
|
378
554
|
pullRequestsPath(repository, id),
|
|
379
555
|
body,
|