From d9e49820ebb0310db0fa0665bbd0db1e473749cb Mon Sep 17 00:00:00 2001 From: Zhiyu You Date: Wed, 8 Apr 2026 11:21:33 +0800 Subject: [PATCH 01/68] feat(v4): add core-next & cli-next packages with integration/E2E tests and CI workflows - Add core-next engine: operations, drivers, DA manifest, templates, lifecycle - Add cli-next CLI: Commander.js commands, action handlers, telemetry, auth - Add integration tests: 24 core-next + 62 cli-next - Add E2E tests: cli-syntax verification with scaffold tests - Add CI workflow (ci-next.yml): build, lint, format, unit+integration tests - Add E2E workflow (e2e-test-next.yml): matrix test execution with failure summary - Add instruction files, skills, and plans for v4 development --- .agents/skills/continual-learning/SKILL.md | 79 + .../skills/frontend-design-review/SKILL.md | 138 + .../references/pattern-examples.md | 21 + .../references/quick-checklist.md | 38 + .../references/review-output-format.md | 68 + .../references/review-type-modifiers.md | 31 + .agents/skills/microsoft-docs/SKILL.md | 77 + .agents/skills/skill-creator/SKILL.md | 613 ++++ .../references/acceptance-criteria.md | 499 +++ .../references/azure-sdk-patterns.md | 539 +++ .../references/output-patterns.md | 187 + .../skill-creator/references/workflows.md | 145 + .../skill-creator/scripts/init_skill.py | 303 ++ .../skill-creator/scripts/package_skill.py | 110 + .../skill-creator/scripts/quick_validate.py | 95 + .dev/features.json | 228 ++ .dev/features.schema.json | 132 + ...03-24-feature-tracked-integration-tests.md | 33 + .../2026-03-24-integration-test-refactor.md | 45 + .dev/plans/2026-03-27-full-stack-refactor.md | 48 + .dev/plans/2026-03-27-phase2-cli-rewrite.md | 50 + .dev/plans/2026-04-02-lifecycle-e2e.md | 85 + .dev/plans/2026-04-07-add-commands.md | 31 + .github/actions/setup-project/action.yml | 3 + .github/copilot-instructions.md | 91 - .github/instructions/api.instructions.md | 101 + .github/instructions/cli.instructions.md | 379 ++ .github/instructions/codebase.instructions.md | 343 ++ .github/instructions/features.instructions.md | 180 + .github/instructions/fx-core.instructions.md | 612 ++++ .github/instructions/manifest.instructions.md | 222 -- .../instructions/templates.instructions.md | 141 + .../vscode-extension.instructions.md | 152 + .github/skills/dev-test-next/SKILL.md | 235 ++ .github/skills/dev-workflow/SKILL.md | 206 ++ .../dev-workflow/references/architecture.md | 141 + .../dev-workflow/references/templates.md | 84 + .github/skills/docs-guard/SKILL.md | 108 + .github/skills/feature-flag-refactor/SKILL.md | 174 + .github/skills/plan-tracker/SKILL.md | 79 + .github/skills/session-review/SKILL.md | 80 + .github/workflows/cd.yml | 10 +- .github/workflows/ci-next.yml | 150 + .github/workflows/e2e-test-next.yml | 335 ++ .github/workflows/unit-test.yml | 2 +- .gitignore | 3 + package.json | 1 + packages/cli-next/.mocharc.js | 16 + packages/cli-next/.nycrc | 9 + packages/cli-next/.prettierrc.js | 3 + packages/cli-next/cli.js | 9 + packages/cli-next/cliold.js | 9 + packages/cli-next/eslint.config.mjs | 17 + packages/cli-next/package.json | 80 + packages/cli-next/pnpm-lock.yaml | 3220 +++++++++++++++++ packages/cli-next/src/actions/addAction.ts | 47 + .../cli-next/src/actions/addAuthConfig.ts | 52 + .../cli-next/src/actions/addCapability.ts | 52 + .../cli-next/src/actions/createProject.ts | 71 + packages/cli-next/src/actions/environment.ts | 33 + packages/cli-next/src/actions/lifecycle.ts | 68 + .../cli-next/src/actions/listTemplates.ts | 26 + packages/cli-next/src/actions/m365Sideload.ts | 31 + .../src/actions/setSensitivityLabel.ts | 46 + packages/cli-next/src/actions/teamsapp.ts | 72 + packages/cli-next/src/auth/azureLogin.ts | 292 ++ packages/cli-next/src/auth/azureLoginCI.ts | 193 + packages/cli-next/src/auth/cacheAccess.ts | 272 ++ packages/cli-next/src/auth/codeFlowLogin.ts | 218 ++ packages/cli-next/src/auth/constants.ts | 41 + packages/cli-next/src/auth/index.ts | 28 + packages/cli-next/src/auth/m365Login.ts | 147 + packages/cli-next/src/auth/utils.ts | 75 + packages/cli-next/src/commands/account.ts | 125 + packages/cli-next/src/commands/add.ts | 93 + packages/cli-next/src/commands/entraApp.ts | 26 + packages/cli-next/src/commands/env.ts | 64 + packages/cli-next/src/commands/factory.ts | 161 + packages/cli-next/src/commands/index.ts | 48 + packages/cli-next/src/commands/list.ts | 49 + packages/cli-next/src/commands/m365.ts | 54 + packages/cli-next/src/commands/misc.ts | 77 + packages/cli-next/src/commands/permission.ts | 37 + packages/cli-next/src/commands/project.ts | 112 + packages/cli-next/src/commands/regenerate.ts | 23 + packages/cli-next/src/commands/teamsapp.ts | 96 + packages/cli-next/src/context.ts | 52 + packages/cli-next/src/error.ts | 60 + packages/cli-next/src/handler.ts | 108 + packages/cli-next/src/index.ts | 69 + packages/cli-next/src/logger.ts | 108 + packages/cli-next/src/output/colorize.ts | 42 + packages/cli-next/src/output/formatter.ts | 49 + packages/cli-next/src/output/index.ts | 5 + .../src/telemetry/appInsightsTransport.ts | 62 + packages/cli-next/src/telemetry/index.ts | 118 + packages/cli-next/src/telemetry/sanitize.ts | 68 + packages/cli-next/src/ui/index.ts | 4 + packages/cli-next/src/ui/userInteraction.ts | 256 ++ packages/cli-next/tests/e2e/clean.ts | 32 + .../cli-next/tests/e2e/cli-syntax.test.ts | 145 + .../cli-next/tests/e2e/cli-syntax.tests.ts | 145 + packages/cli-next/tests/e2e/infra/azure.ts | 167 + .../cli-next/tests/e2e/infra/checkpoint.ts | 88 + packages/cli-next/tests/e2e/infra/config.ts | 140 + .../cli-next/tests/e2e/infra/testContext.ts | 106 + packages/cli-next/tests/e2e/infra/tracer.ts | 401 ++ .../cli-next/tests/e2e/infra/validators.ts | 154 + packages/cli-next/tests/e2e/lifecycle.test.ts | 218 ++ packages/cli-next/tests/e2e/setup.ts | 37 + .../tests/integration/addCommands.test.ts | 225 ++ .../integration/commandExecution.test.ts | 82 + .../integration/coreNextIntegration.test.ts | 181 + .../tests/integration/driverCoverage.test.ts | 93 + .../tests/integration/helpers/driverStubs.ts | 125 + .../integration/helpers/featureRegistry.ts | 140 + .../tests/integration/helpers/mockContext.ts | 103 + .../integration/helpers/templateYamlLoader.ts | 108 + .../pipeline/deployPipeline.test.ts | 111 + .../pipeline/provisionPipeline.test.ts | 115 + .../pipeline/publishPipeline.test.ts | 112 + packages/cli-next/tests/unit/actions.test.ts | 246 ++ packages/cli-next/tests/unit/commands.test.ts | 39 + packages/cli-next/tests/unit/context.test.ts | 35 + .../cli-next/tests/unit/da-actions.test.ts | 239 ++ packages/cli-next/tests/unit/error.test.ts | 69 + packages/cli-next/tests/unit/factory.test.ts | 263 ++ packages/cli-next/tests/unit/handler.test.ts | 73 + packages/cli-next/tests/unit/output.test.ts | 60 + .../tests/unit/telemetry-reporter.test.ts | 108 + .../tests/unit/telemetry-sanitize.test.ts | 80 + packages/cli-next/tsconfig.eslint.json | 8 + packages/cli-next/tsconfig.json | 23 + packages/cli-next/tsconfig.tsbuildinfo | 1 + packages/core-next/.mocharc.js | 16 + packages/core-next/.nycrc | 10 + packages/core-next/.prettierrc.js | 3 + packages/core-next/eslint.config.mjs | 19 + packages/core-next/package.json | 62 + packages/core-next/pnpm-lock.yaml | 2334 ++++++++++++ packages/core-next/src/api/cli.ts | 84 + packages/core-next/src/api/constants.ts | 96 + packages/core-next/src/api/context.ts | 16 + packages/core-next/src/api/error.ts | 129 + packages/core-next/src/api/generator.ts | 20 + packages/core-next/src/api/index.ts | 17 + packages/core-next/src/api/qm/index.ts | 6 + packages/core-next/src/api/qm/question.ts | 177 + packages/core-next/src/api/qm/ui.ts | 155 + packages/core-next/src/api/qm/validation.ts | 49 + packages/core-next/src/api/types.ts | 85 + packages/core-next/src/api/utils/crypto.ts | 10 + packages/core-next/src/api/utils/exp.ts | 10 + packages/core-next/src/api/utils/index.ts | 27 + packages/core-next/src/api/utils/log.ts | 32 + packages/core-next/src/api/utils/login.ts | 126 + packages/core-next/src/api/utils/telemetry.ts | 23 + packages/core-next/src/api/utils/tree.ts | 34 + .../core-next/src/clients/azure/client.ts | 205 ++ packages/core-next/src/clients/azure/index.ts | 21 + packages/core-next/src/clients/azure/types.ts | 98 + .../core-next/src/clients/graphApi/client.ts | 161 + .../core-next/src/clients/graphApi/index.ts | 5 + .../core-next/src/clients/graphApi/types.ts | 130 + packages/core-next/src/clients/index.ts | 7 + packages/core-next/src/clients/m365/index.ts | 4 + .../src/clients/m365/packageService.ts | 191 + .../src/clients/teamsDevPortal/client.ts | 384 ++ .../src/clients/teamsDevPortal/index.ts | 5 + .../src/clients/teamsDevPortal/types.ts | 120 + packages/core-next/src/core/context.ts | 69 + packages/core-next/src/core/error.ts | 45 + packages/core-next/src/core/index.ts | 6 + packages/core-next/src/core/operation.ts | 115 + .../src/declarativeAgent/actions/addAction.ts | 174 + .../actions/addActionFromMCP.ts | 140 + .../src/declarativeAgent/actions/index.ts | 6 + .../declarativeAgent/actions/removeAction.ts | 41 + .../src/declarativeAgent/auth/authInjector.ts | 296 ++ .../src/declarativeAgent/auth/index.ts | 9 + .../declarativeAgent/capabilities/index.ts | 4 + .../capabilities/sensitivityLabel.ts | 63 + .../core-next/src/declarativeAgent/index.ts | 76 + .../knowledge/addKnowledge.ts | 79 + .../knowledge/embeddedKnowledge.ts | 54 + .../knowledge/graphConnector.ts | 36 + .../src/declarativeAgent/knowledge/index.ts | 10 + .../knowledge/oneDriveSharePoint.ts | 66 + .../declarativeAgent/knowledge/webSearch.ts | 67 + .../declarativeAgent/manifest/discovery.ts | 54 + .../src/declarativeAgent/manifest/index.ts | 5 + .../src/declarativeAgent/manifest/resolver.ts | 59 + .../src/declarativeAgent/operations.ts | 224 ++ .../core-next/src/declarativeAgent/types.ts | 149 + .../src/drivers/builtin/aadApp/create.ts | 142 + .../src/drivers/builtin/aadApp/update.ts | 126 + .../src/drivers/builtin/apiKey/register.ts | 167 + .../src/drivers/builtin/arm/deploy.ts | 231 ++ .../builtin/azureAppService/zipDeploy.ts | 382 ++ .../builtin/azureFunctions/zipDeploy.ts | 30 + .../src/drivers/builtin/botAadApp/create.ts | 100 + .../drivers/builtin/botFramework/create.ts | 106 + .../src/drivers/builtin/cli/runNpmCommand.ts | 118 + .../file/createOrUpdateEnvironmentFile.ts | 98 + .../builtin/file/createOrUpdateJsonFile.ts | 96 + .../core-next/src/drivers/builtin/index.ts | 85 + .../src/drivers/builtin/oauth/register.ts | 176 + .../src/drivers/builtin/script/run.ts | 180 + .../src/drivers/builtin/teamsApp/configure.ts | 112 + .../src/drivers/builtin/teamsApp/create.ts | 127 + .../drivers/builtin/teamsApp/extendToM365.ts | 98 + .../builtin/teamsApp/publishAppPackage.ts | 116 + .../src/drivers/builtin/teamsApp/update.ts | 101 + .../builtin/teamsApp/validateAppPackage.ts | 74 + .../builtin/teamsApp/validateManifest.ts | 37 + .../drivers/builtin/teamsApp/zipAppPackage.ts | 44 + .../core-next/src/drivers/createDriver.ts | 113 + packages/core-next/src/drivers/index.ts | 7 + packages/core-next/src/drivers/registry.ts | 60 + packages/core-next/src/drivers/types.ts | 48 + .../core-next/src/environment/envManager.ts | 168 + packages/core-next/src/environment/index.ts | 10 + packages/core-next/src/featureFlags/flags.ts | 57 + packages/core-next/src/featureFlags/index.ts | 6 + .../core-next/src/featureFlags/registry.ts | 57 + packages/core-next/src/featureFlags/types.ts | 31 + packages/core-next/src/folder.ts | 13 + packages/core-next/src/http/httpClient.ts | 82 + packages/core-next/src/http/index.ts | 5 + packages/core-next/src/http/retry.ts | 69 + packages/core-next/src/index.ts | 53 + packages/core-next/src/lifecycle/analyze.ts | 109 + packages/core-next/src/lifecycle/executor.ts | 116 + packages/core-next/src/lifecycle/index.ts | 36 + .../core-next/src/lifecycle/operations.ts | 365 ++ packages/core-next/src/lifecycle/parser.ts | 146 + .../core-next/src/lifecycle/prerequisites.ts | 331 ++ packages/core-next/src/lifecycle/progress.ts | 47 + packages/core-next/src/lifecycle/resolver.ts | 56 + packages/core-next/src/lifecycle/types.ts | 194 + packages/core-next/src/localization/index.ts | 4 + .../core-next/src/localization/localizer.ts | 95 + packages/core-next/src/manifest/index.ts | 27 + .../core-next/src/manifest/readManifest.ts | 124 + packages/core-next/src/manifest/resolve.ts | 296 ++ packages/core-next/src/manifest/telemetry.ts | 54 + packages/core-next/src/manifest/types.ts | 57 + packages/core-next/src/manifest/validate.ts | 37 + packages/core-next/src/project/create.ts | 190 + packages/core-next/src/project/index.ts | 4 + .../src/questions/commonQuestions.ts | 285 ++ packages/core-next/src/questions/index.ts | 28 + .../core-next/src/questions/questionNames.ts | 41 + packages/core-next/src/questions/traverse.ts | 357 ++ .../core-next/src/questions/treeBuilder.ts | 170 + packages/core-next/src/secretMasker/index.ts | 5 + .../core-next/src/secretMasker/keywords.ts | 95 + packages/core-next/src/secretMasker/masker.ts | 49 + packages/core-next/src/teamsApp/index.ts | 12 + packages/core-next/src/teamsApp/operations.ts | 271 ++ .../core-next/src/teamsApp/packageBuilder.ts | 691 ++++ .../core-next/src/telemetry/correlation.ts | 22 + .../src/telemetry/errorProperties.ts | 24 + packages/core-next/src/telemetry/helpers.ts | 69 + packages/core-next/src/telemetry/index.ts | 8 + .../src/telemetry/instrumentOperation.ts | 51 + packages/core-next/src/telemetry/types.ts | 54 + .../src/templates/descriptors/aiAgent.ts | 116 + .../src/templates/descriptors/bot.ts | 125 + .../src/templates/descriptors/connector.ts | 68 + .../templates/descriptors/declarativeAgent.ts | 178 + .../src/templates/descriptors/engineAgent.ts | 84 + .../src/templates/descriptors/index.ts | 50 + .../templates/descriptors/messageExtension.ts | 110 + .../src/templates/descriptors/openApi.ts | 70 + .../src/templates/descriptors/tab.ts | 97 + packages/core-next/src/templates/index.ts | 39 + .../core-next/src/templates/openApi/index.ts | 12 + .../src/templates/openApi/scaffoldFn.ts | 125 + .../templates/openApi/specParserAdapter.ts | 124 + packages/core-next/src/templates/registry.ts | 81 + .../src/templates/scaffold/download.ts | 107 + .../core-next/src/templates/scaffold/index.ts | 9 + .../src/templates/scaffold/render.ts | 140 + .../src/templates/scaffold/replaceMap.ts | 63 + .../src/templates/scaffold/scaffolder.ts | 135 + .../core-next/src/templates/scaffold/types.ts | 80 + packages/core-next/src/templates/types.ts | 138 + .../core-next/templates/fallback/common.zip | Bin 0 -> 553861 bytes .../core-next/templates/fallback/csharp.zip | Bin 0 -> 1064333 bytes packages/core-next/templates/fallback/js.zip | Bin 0 -> 407081 bytes .../core-next/templates/fallback/python.zip | Bin 0 -> 293109 bytes packages/core-next/templates/fallback/ts.zip | Bin 0 -> 1537070 bytes .../tests/integration/crossCutting.test.ts | 109 + .../tests/integration/daOperations.test.ts | 252 ++ .../tests/integration/daPackaging.test.ts | 215 ++ .../integration/lifecycleExecution.test.ts | 288 ++ .../integration/operationPipeline.test.ts | 153 + .../tests/unit/clients/azureArm.test.ts | 227 ++ .../tests/unit/clients/graphApi.test.ts | 209 ++ .../tests/unit/clients/teamsDevPortal.test.ts | 405 +++ .../core-next/tests/unit/core/context.test.ts | 53 + .../core-next/tests/unit/core/error.test.ts | 42 + .../tests/unit/core/operation.test.ts | 87 + .../unit/declarativeAgent/discovery.test.ts | 90 + .../drivers/builtin/authPluginDrivers.test.ts | 513 +++ .../drivers/builtin/azureInfraDrivers.test.ts | 511 +++ .../builtin/entraAndBotDrivers.test.ts | 507 +++ .../unit/drivers/builtin/fileDrivers.test.ts | 216 ++ .../unit/drivers/builtin/registration.test.ts | 49 + .../unit/drivers/builtin/scriptDriver.test.ts | 173 + .../drivers/builtin/teamsAppDrivers.test.ts | 204 ++ .../drivers/builtin/teamsAppPlatform.test.ts | 367 ++ .../tests/unit/drivers/createDriver.test.ts | 201 + .../tests/unit/drivers/registry.test.ts | 36 + .../tests/unit/featureFlags/registry.test.ts | 116 + .../tests/unit/lifecycle/analyze.test.ts | 105 + .../tests/unit/lifecycle/operations.test.ts | 555 +++ .../unit/lifecycle/prerequisites.test.ts | 528 +++ .../tests/unit/lifecycle/progress.test.ts | 101 + .../tests/unit/manifest/readManifest.test.ts | 140 + .../tests/unit/manifest/resolve.test.ts | 273 ++ .../unit/questions/commonQuestions.test.ts | 140 + .../tests/unit/questions/traverse.test.ts | 432 +++ .../questions/traverseIntegration.test.ts | 189 + .../tests/unit/questions/treeBuilder.test.ts | 239 ++ .../tests/unit/secretMasker/masker.test.ts | 96 + .../tests/unit/teamsApp/extendToM365.test.ts | 84 + .../unit/teamsApp/packageBuilder.test.ts | 298 ++ .../tests/unit/telemetry/correlation.test.ts | 33 + .../unit/telemetry/errorProperties.test.ts | 40 + .../tests/unit/telemetry/helpers.test.ts | 76 + .../telemetry/instrumentOperation.test.ts | 56 + .../tests/unit/templates/descriptors.test.ts | 238 ++ .../tests/unit/templates/openApi.test.ts | 229 ++ .../tests/unit/templates/registry.test.ts | 70 + packages/core-next/tests/unit/testHelper.ts | 54 + packages/core-next/tsconfig.eslint.json | 8 + packages/core-next/tsconfig.json | 23 + packages/core-next/tsconfig.tsbuildinfo | 1 + .../eslint-plugin-teamsfx/config/shared.mjs | 10 +- pnpm-workspace.yaml | 2 + skills-lock.json | 25 + 343 files changed, 44258 insertions(+), 316 deletions(-) create mode 100644 .agents/skills/continual-learning/SKILL.md create mode 100644 .agents/skills/frontend-design-review/SKILL.md create mode 100644 .agents/skills/frontend-design-review/references/pattern-examples.md create mode 100644 .agents/skills/frontend-design-review/references/quick-checklist.md create mode 100644 .agents/skills/frontend-design-review/references/review-output-format.md create mode 100644 .agents/skills/frontend-design-review/references/review-type-modifiers.md create mode 100644 .agents/skills/microsoft-docs/SKILL.md create mode 100644 .agents/skills/skill-creator/SKILL.md create mode 100644 .agents/skills/skill-creator/references/acceptance-criteria.md create mode 100644 .agents/skills/skill-creator/references/azure-sdk-patterns.md create mode 100644 .agents/skills/skill-creator/references/output-patterns.md create mode 100644 .agents/skills/skill-creator/references/workflows.md create mode 100644 .agents/skills/skill-creator/scripts/init_skill.py create mode 100644 .agents/skills/skill-creator/scripts/package_skill.py create mode 100644 .agents/skills/skill-creator/scripts/quick_validate.py create mode 100644 .dev/features.json create mode 100644 .dev/features.schema.json create mode 100644 .dev/plans/2026-03-24-feature-tracked-integration-tests.md create mode 100644 .dev/plans/2026-03-24-integration-test-refactor.md create mode 100644 .dev/plans/2026-03-27-full-stack-refactor.md create mode 100644 .dev/plans/2026-03-27-phase2-cli-rewrite.md create mode 100644 .dev/plans/2026-04-02-lifecycle-e2e.md create mode 100644 .dev/plans/2026-04-07-add-commands.md delete mode 100644 .github/copilot-instructions.md create mode 100644 .github/instructions/api.instructions.md create mode 100644 .github/instructions/cli.instructions.md create mode 100644 .github/instructions/codebase.instructions.md create mode 100644 .github/instructions/features.instructions.md create mode 100644 .github/instructions/fx-core.instructions.md delete mode 100644 .github/instructions/manifest.instructions.md create mode 100644 .github/instructions/templates.instructions.md create mode 100644 .github/instructions/vscode-extension.instructions.md create mode 100644 .github/skills/dev-test-next/SKILL.md create mode 100644 .github/skills/dev-workflow/SKILL.md create mode 100644 .github/skills/dev-workflow/references/architecture.md create mode 100644 .github/skills/dev-workflow/references/templates.md create mode 100644 .github/skills/docs-guard/SKILL.md create mode 100644 .github/skills/feature-flag-refactor/SKILL.md create mode 100644 .github/skills/plan-tracker/SKILL.md create mode 100644 .github/skills/session-review/SKILL.md create mode 100644 .github/workflows/ci-next.yml create mode 100644 .github/workflows/e2e-test-next.yml create mode 100644 packages/cli-next/.mocharc.js create mode 100644 packages/cli-next/.nycrc create mode 100644 packages/cli-next/.prettierrc.js create mode 100644 packages/cli-next/cli.js create mode 100644 packages/cli-next/cliold.js create mode 100644 packages/cli-next/eslint.config.mjs create mode 100644 packages/cli-next/package.json create mode 100644 packages/cli-next/pnpm-lock.yaml create mode 100644 packages/cli-next/src/actions/addAction.ts create mode 100644 packages/cli-next/src/actions/addAuthConfig.ts create mode 100644 packages/cli-next/src/actions/addCapability.ts create mode 100644 packages/cli-next/src/actions/createProject.ts create mode 100644 packages/cli-next/src/actions/environment.ts create mode 100644 packages/cli-next/src/actions/lifecycle.ts create mode 100644 packages/cli-next/src/actions/listTemplates.ts create mode 100644 packages/cli-next/src/actions/m365Sideload.ts create mode 100644 packages/cli-next/src/actions/setSensitivityLabel.ts create mode 100644 packages/cli-next/src/actions/teamsapp.ts create mode 100644 packages/cli-next/src/auth/azureLogin.ts create mode 100644 packages/cli-next/src/auth/azureLoginCI.ts create mode 100644 packages/cli-next/src/auth/cacheAccess.ts create mode 100644 packages/cli-next/src/auth/codeFlowLogin.ts create mode 100644 packages/cli-next/src/auth/constants.ts create mode 100644 packages/cli-next/src/auth/index.ts create mode 100644 packages/cli-next/src/auth/m365Login.ts create mode 100644 packages/cli-next/src/auth/utils.ts create mode 100644 packages/cli-next/src/commands/account.ts create mode 100644 packages/cli-next/src/commands/add.ts create mode 100644 packages/cli-next/src/commands/entraApp.ts create mode 100644 packages/cli-next/src/commands/env.ts create mode 100644 packages/cli-next/src/commands/factory.ts create mode 100644 packages/cli-next/src/commands/index.ts create mode 100644 packages/cli-next/src/commands/list.ts create mode 100644 packages/cli-next/src/commands/m365.ts create mode 100644 packages/cli-next/src/commands/misc.ts create mode 100644 packages/cli-next/src/commands/permission.ts create mode 100644 packages/cli-next/src/commands/project.ts create mode 100644 packages/cli-next/src/commands/regenerate.ts create mode 100644 packages/cli-next/src/commands/teamsapp.ts create mode 100644 packages/cli-next/src/context.ts create mode 100644 packages/cli-next/src/error.ts create mode 100644 packages/cli-next/src/handler.ts create mode 100644 packages/cli-next/src/index.ts create mode 100644 packages/cli-next/src/logger.ts create mode 100644 packages/cli-next/src/output/colorize.ts create mode 100644 packages/cli-next/src/output/formatter.ts create mode 100644 packages/cli-next/src/output/index.ts create mode 100644 packages/cli-next/src/telemetry/appInsightsTransport.ts create mode 100644 packages/cli-next/src/telemetry/index.ts create mode 100644 packages/cli-next/src/telemetry/sanitize.ts create mode 100644 packages/cli-next/src/ui/index.ts create mode 100644 packages/cli-next/src/ui/userInteraction.ts create mode 100644 packages/cli-next/tests/e2e/clean.ts create mode 100644 packages/cli-next/tests/e2e/cli-syntax.test.ts create mode 100644 packages/cli-next/tests/e2e/cli-syntax.tests.ts create mode 100644 packages/cli-next/tests/e2e/infra/azure.ts create mode 100644 packages/cli-next/tests/e2e/infra/checkpoint.ts create mode 100644 packages/cli-next/tests/e2e/infra/config.ts create mode 100644 packages/cli-next/tests/e2e/infra/testContext.ts create mode 100644 packages/cli-next/tests/e2e/infra/tracer.ts create mode 100644 packages/cli-next/tests/e2e/infra/validators.ts create mode 100644 packages/cli-next/tests/e2e/lifecycle.test.ts create mode 100644 packages/cli-next/tests/e2e/setup.ts create mode 100644 packages/cli-next/tests/integration/addCommands.test.ts create mode 100644 packages/cli-next/tests/integration/commandExecution.test.ts create mode 100644 packages/cli-next/tests/integration/coreNextIntegration.test.ts create mode 100644 packages/cli-next/tests/integration/driverCoverage.test.ts create mode 100644 packages/cli-next/tests/integration/helpers/driverStubs.ts create mode 100644 packages/cli-next/tests/integration/helpers/featureRegistry.ts create mode 100644 packages/cli-next/tests/integration/helpers/mockContext.ts create mode 100644 packages/cli-next/tests/integration/helpers/templateYamlLoader.ts create mode 100644 packages/cli-next/tests/integration/pipeline/deployPipeline.test.ts create mode 100644 packages/cli-next/tests/integration/pipeline/provisionPipeline.test.ts create mode 100644 packages/cli-next/tests/integration/pipeline/publishPipeline.test.ts create mode 100644 packages/cli-next/tests/unit/actions.test.ts create mode 100644 packages/cli-next/tests/unit/commands.test.ts create mode 100644 packages/cli-next/tests/unit/context.test.ts create mode 100644 packages/cli-next/tests/unit/da-actions.test.ts create mode 100644 packages/cli-next/tests/unit/error.test.ts create mode 100644 packages/cli-next/tests/unit/factory.test.ts create mode 100644 packages/cli-next/tests/unit/handler.test.ts create mode 100644 packages/cli-next/tests/unit/output.test.ts create mode 100644 packages/cli-next/tests/unit/telemetry-reporter.test.ts create mode 100644 packages/cli-next/tests/unit/telemetry-sanitize.test.ts create mode 100644 packages/cli-next/tsconfig.eslint.json create mode 100644 packages/cli-next/tsconfig.json create mode 100644 packages/cli-next/tsconfig.tsbuildinfo create mode 100644 packages/core-next/.mocharc.js create mode 100644 packages/core-next/.nycrc create mode 100644 packages/core-next/.prettierrc.js create mode 100644 packages/core-next/eslint.config.mjs create mode 100644 packages/core-next/package.json create mode 100644 packages/core-next/pnpm-lock.yaml create mode 100644 packages/core-next/src/api/cli.ts create mode 100644 packages/core-next/src/api/constants.ts create mode 100644 packages/core-next/src/api/context.ts create mode 100644 packages/core-next/src/api/error.ts create mode 100644 packages/core-next/src/api/generator.ts create mode 100644 packages/core-next/src/api/index.ts create mode 100644 packages/core-next/src/api/qm/index.ts create mode 100644 packages/core-next/src/api/qm/question.ts create mode 100644 packages/core-next/src/api/qm/ui.ts create mode 100644 packages/core-next/src/api/qm/validation.ts create mode 100644 packages/core-next/src/api/types.ts create mode 100644 packages/core-next/src/api/utils/crypto.ts create mode 100644 packages/core-next/src/api/utils/exp.ts create mode 100644 packages/core-next/src/api/utils/index.ts create mode 100644 packages/core-next/src/api/utils/log.ts create mode 100644 packages/core-next/src/api/utils/login.ts create mode 100644 packages/core-next/src/api/utils/telemetry.ts create mode 100644 packages/core-next/src/api/utils/tree.ts create mode 100644 packages/core-next/src/clients/azure/client.ts create mode 100644 packages/core-next/src/clients/azure/index.ts create mode 100644 packages/core-next/src/clients/azure/types.ts create mode 100644 packages/core-next/src/clients/graphApi/client.ts create mode 100644 packages/core-next/src/clients/graphApi/index.ts create mode 100644 packages/core-next/src/clients/graphApi/types.ts create mode 100644 packages/core-next/src/clients/index.ts create mode 100644 packages/core-next/src/clients/m365/index.ts create mode 100644 packages/core-next/src/clients/m365/packageService.ts create mode 100644 packages/core-next/src/clients/teamsDevPortal/client.ts create mode 100644 packages/core-next/src/clients/teamsDevPortal/index.ts create mode 100644 packages/core-next/src/clients/teamsDevPortal/types.ts create mode 100644 packages/core-next/src/core/context.ts create mode 100644 packages/core-next/src/core/error.ts create mode 100644 packages/core-next/src/core/index.ts create mode 100644 packages/core-next/src/core/operation.ts create mode 100644 packages/core-next/src/declarativeAgent/actions/addAction.ts create mode 100644 packages/core-next/src/declarativeAgent/actions/addActionFromMCP.ts create mode 100644 packages/core-next/src/declarativeAgent/actions/index.ts create mode 100644 packages/core-next/src/declarativeAgent/actions/removeAction.ts create mode 100644 packages/core-next/src/declarativeAgent/auth/authInjector.ts create mode 100644 packages/core-next/src/declarativeAgent/auth/index.ts create mode 100644 packages/core-next/src/declarativeAgent/capabilities/index.ts create mode 100644 packages/core-next/src/declarativeAgent/capabilities/sensitivityLabel.ts create mode 100644 packages/core-next/src/declarativeAgent/index.ts create mode 100644 packages/core-next/src/declarativeAgent/knowledge/addKnowledge.ts create mode 100644 packages/core-next/src/declarativeAgent/knowledge/embeddedKnowledge.ts create mode 100644 packages/core-next/src/declarativeAgent/knowledge/graphConnector.ts create mode 100644 packages/core-next/src/declarativeAgent/knowledge/index.ts create mode 100644 packages/core-next/src/declarativeAgent/knowledge/oneDriveSharePoint.ts create mode 100644 packages/core-next/src/declarativeAgent/knowledge/webSearch.ts create mode 100644 packages/core-next/src/declarativeAgent/manifest/discovery.ts create mode 100644 packages/core-next/src/declarativeAgent/manifest/index.ts create mode 100644 packages/core-next/src/declarativeAgent/manifest/resolver.ts create mode 100644 packages/core-next/src/declarativeAgent/operations.ts create mode 100644 packages/core-next/src/declarativeAgent/types.ts create mode 100644 packages/core-next/src/drivers/builtin/aadApp/create.ts create mode 100644 packages/core-next/src/drivers/builtin/aadApp/update.ts create mode 100644 packages/core-next/src/drivers/builtin/apiKey/register.ts create mode 100644 packages/core-next/src/drivers/builtin/arm/deploy.ts create mode 100644 packages/core-next/src/drivers/builtin/azureAppService/zipDeploy.ts create mode 100644 packages/core-next/src/drivers/builtin/azureFunctions/zipDeploy.ts create mode 100644 packages/core-next/src/drivers/builtin/botAadApp/create.ts create mode 100644 packages/core-next/src/drivers/builtin/botFramework/create.ts create mode 100644 packages/core-next/src/drivers/builtin/cli/runNpmCommand.ts create mode 100644 packages/core-next/src/drivers/builtin/file/createOrUpdateEnvironmentFile.ts create mode 100644 packages/core-next/src/drivers/builtin/file/createOrUpdateJsonFile.ts create mode 100644 packages/core-next/src/drivers/builtin/index.ts create mode 100644 packages/core-next/src/drivers/builtin/oauth/register.ts create mode 100644 packages/core-next/src/drivers/builtin/script/run.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/configure.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/create.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/extendToM365.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/publishAppPackage.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/update.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/validateAppPackage.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/validateManifest.ts create mode 100644 packages/core-next/src/drivers/builtin/teamsApp/zipAppPackage.ts create mode 100644 packages/core-next/src/drivers/createDriver.ts create mode 100644 packages/core-next/src/drivers/index.ts create mode 100644 packages/core-next/src/drivers/registry.ts create mode 100644 packages/core-next/src/drivers/types.ts create mode 100644 packages/core-next/src/environment/envManager.ts create mode 100644 packages/core-next/src/environment/index.ts create mode 100644 packages/core-next/src/featureFlags/flags.ts create mode 100644 packages/core-next/src/featureFlags/index.ts create mode 100644 packages/core-next/src/featureFlags/registry.ts create mode 100644 packages/core-next/src/featureFlags/types.ts create mode 100644 packages/core-next/src/folder.ts create mode 100644 packages/core-next/src/http/httpClient.ts create mode 100644 packages/core-next/src/http/index.ts create mode 100644 packages/core-next/src/http/retry.ts create mode 100644 packages/core-next/src/index.ts create mode 100644 packages/core-next/src/lifecycle/analyze.ts create mode 100644 packages/core-next/src/lifecycle/executor.ts create mode 100644 packages/core-next/src/lifecycle/index.ts create mode 100644 packages/core-next/src/lifecycle/operations.ts create mode 100644 packages/core-next/src/lifecycle/parser.ts create mode 100644 packages/core-next/src/lifecycle/prerequisites.ts create mode 100644 packages/core-next/src/lifecycle/progress.ts create mode 100644 packages/core-next/src/lifecycle/resolver.ts create mode 100644 packages/core-next/src/lifecycle/types.ts create mode 100644 packages/core-next/src/localization/index.ts create mode 100644 packages/core-next/src/localization/localizer.ts create mode 100644 packages/core-next/src/manifest/index.ts create mode 100644 packages/core-next/src/manifest/readManifest.ts create mode 100644 packages/core-next/src/manifest/resolve.ts create mode 100644 packages/core-next/src/manifest/telemetry.ts create mode 100644 packages/core-next/src/manifest/types.ts create mode 100644 packages/core-next/src/manifest/validate.ts create mode 100644 packages/core-next/src/project/create.ts create mode 100644 packages/core-next/src/project/index.ts create mode 100644 packages/core-next/src/questions/commonQuestions.ts create mode 100644 packages/core-next/src/questions/index.ts create mode 100644 packages/core-next/src/questions/questionNames.ts create mode 100644 packages/core-next/src/questions/traverse.ts create mode 100644 packages/core-next/src/questions/treeBuilder.ts create mode 100644 packages/core-next/src/secretMasker/index.ts create mode 100644 packages/core-next/src/secretMasker/keywords.ts create mode 100644 packages/core-next/src/secretMasker/masker.ts create mode 100644 packages/core-next/src/teamsApp/index.ts create mode 100644 packages/core-next/src/teamsApp/operations.ts create mode 100644 packages/core-next/src/teamsApp/packageBuilder.ts create mode 100644 packages/core-next/src/telemetry/correlation.ts create mode 100644 packages/core-next/src/telemetry/errorProperties.ts create mode 100644 packages/core-next/src/telemetry/helpers.ts create mode 100644 packages/core-next/src/telemetry/index.ts create mode 100644 packages/core-next/src/telemetry/instrumentOperation.ts create mode 100644 packages/core-next/src/telemetry/types.ts create mode 100644 packages/core-next/src/templates/descriptors/aiAgent.ts create mode 100644 packages/core-next/src/templates/descriptors/bot.ts create mode 100644 packages/core-next/src/templates/descriptors/connector.ts create mode 100644 packages/core-next/src/templates/descriptors/declarativeAgent.ts create mode 100644 packages/core-next/src/templates/descriptors/engineAgent.ts create mode 100644 packages/core-next/src/templates/descriptors/index.ts create mode 100644 packages/core-next/src/templates/descriptors/messageExtension.ts create mode 100644 packages/core-next/src/templates/descriptors/openApi.ts create mode 100644 packages/core-next/src/templates/descriptors/tab.ts create mode 100644 packages/core-next/src/templates/index.ts create mode 100644 packages/core-next/src/templates/openApi/index.ts create mode 100644 packages/core-next/src/templates/openApi/scaffoldFn.ts create mode 100644 packages/core-next/src/templates/openApi/specParserAdapter.ts create mode 100644 packages/core-next/src/templates/registry.ts create mode 100644 packages/core-next/src/templates/scaffold/download.ts create mode 100644 packages/core-next/src/templates/scaffold/index.ts create mode 100644 packages/core-next/src/templates/scaffold/render.ts create mode 100644 packages/core-next/src/templates/scaffold/replaceMap.ts create mode 100644 packages/core-next/src/templates/scaffold/scaffolder.ts create mode 100644 packages/core-next/src/templates/scaffold/types.ts create mode 100644 packages/core-next/src/templates/types.ts create mode 100644 packages/core-next/templates/fallback/common.zip create mode 100644 packages/core-next/templates/fallback/csharp.zip create mode 100644 packages/core-next/templates/fallback/js.zip create mode 100644 packages/core-next/templates/fallback/python.zip create mode 100644 packages/core-next/templates/fallback/ts.zip create mode 100644 packages/core-next/tests/integration/crossCutting.test.ts create mode 100644 packages/core-next/tests/integration/daOperations.test.ts create mode 100644 packages/core-next/tests/integration/daPackaging.test.ts create mode 100644 packages/core-next/tests/integration/lifecycleExecution.test.ts create mode 100644 packages/core-next/tests/integration/operationPipeline.test.ts create mode 100644 packages/core-next/tests/unit/clients/azureArm.test.ts create mode 100644 packages/core-next/tests/unit/clients/graphApi.test.ts create mode 100644 packages/core-next/tests/unit/clients/teamsDevPortal.test.ts create mode 100644 packages/core-next/tests/unit/core/context.test.ts create mode 100644 packages/core-next/tests/unit/core/error.test.ts create mode 100644 packages/core-next/tests/unit/core/operation.test.ts create mode 100644 packages/core-next/tests/unit/declarativeAgent/discovery.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/authPluginDrivers.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/azureInfraDrivers.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/entraAndBotDrivers.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/fileDrivers.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/registration.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/scriptDriver.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/teamsAppDrivers.test.ts create mode 100644 packages/core-next/tests/unit/drivers/builtin/teamsAppPlatform.test.ts create mode 100644 packages/core-next/tests/unit/drivers/createDriver.test.ts create mode 100644 packages/core-next/tests/unit/drivers/registry.test.ts create mode 100644 packages/core-next/tests/unit/featureFlags/registry.test.ts create mode 100644 packages/core-next/tests/unit/lifecycle/analyze.test.ts create mode 100644 packages/core-next/tests/unit/lifecycle/operations.test.ts create mode 100644 packages/core-next/tests/unit/lifecycle/prerequisites.test.ts create mode 100644 packages/core-next/tests/unit/lifecycle/progress.test.ts create mode 100644 packages/core-next/tests/unit/manifest/readManifest.test.ts create mode 100644 packages/core-next/tests/unit/manifest/resolve.test.ts create mode 100644 packages/core-next/tests/unit/questions/commonQuestions.test.ts create mode 100644 packages/core-next/tests/unit/questions/traverse.test.ts create mode 100644 packages/core-next/tests/unit/questions/traverseIntegration.test.ts create mode 100644 packages/core-next/tests/unit/questions/treeBuilder.test.ts create mode 100644 packages/core-next/tests/unit/secretMasker/masker.test.ts create mode 100644 packages/core-next/tests/unit/teamsApp/extendToM365.test.ts create mode 100644 packages/core-next/tests/unit/teamsApp/packageBuilder.test.ts create mode 100644 packages/core-next/tests/unit/telemetry/correlation.test.ts create mode 100644 packages/core-next/tests/unit/telemetry/errorProperties.test.ts create mode 100644 packages/core-next/tests/unit/telemetry/helpers.test.ts create mode 100644 packages/core-next/tests/unit/telemetry/instrumentOperation.test.ts create mode 100644 packages/core-next/tests/unit/templates/descriptors.test.ts create mode 100644 packages/core-next/tests/unit/templates/openApi.test.ts create mode 100644 packages/core-next/tests/unit/templates/registry.test.ts create mode 100644 packages/core-next/tests/unit/testHelper.ts create mode 100644 packages/core-next/tsconfig.eslint.json create mode 100644 packages/core-next/tsconfig.json create mode 100644 packages/core-next/tsconfig.tsbuildinfo create mode 100644 skills-lock.json diff --git a/.agents/skills/continual-learning/SKILL.md b/.agents/skills/continual-learning/SKILL.md new file mode 100644 index 00000000000..1de328f9abe --- /dev/null +++ b/.agents/skills/continual-learning/SKILL.md @@ -0,0 +1,79 @@ +--- +name: continual-learning +description: Guide for implementing continual learning in AI coding agents — hooks, memory scoping, reflection patterns. Use when setting up learning infrastructure for agents. +--- + +# Continual Learning for AI Coding Agents + +Your agent forgets everything between sessions. Continual learning fixes that. + +## The Loop + +``` +Experience → Capture → Reflect → Persist → Apply + ↑ │ + └───────────────────────────────────────┘ +``` + +## Quick Start + +Install the hook (one step): +```bash +cp -r hooks/continual-learning .github/hooks/ +``` + +Auto-initializes on first session. No config needed. + +## Two-Tier Memory + +**Global** (`~/.copilot/learnings.db`) — follows you across all projects: +- Tool patterns (which tools fail, which work) +- Cross-project conventions +- General coding preferences + +**Local** (`.copilot-memory/learnings.db`) — stays with this repo: +- Project-specific conventions +- Common mistakes for this codebase +- Team preferences + +## How Learnings Get Stored + +### Automatic (via hooks) +The hook observes tool outcomes and detects failure patterns: +``` +Session 1: bash tool fails 4 times → learning stored: "bash frequently fails" +Session 2: hook surfaces that learning at start → agent adjusts approach +``` + +### Agent-native (via store_memory / SQL) +The agent can write learnings directly: +```sql +INSERT INTO learnings (scope, category, content, source) +VALUES ('local', 'convention', 'This project uses Result not exceptions', 'user_correction'); +``` + +Categories: `pattern`, `mistake`, `preference`, `tool_insight` + +### Manual (memory files) +For human-readable, version-controlled knowledge: +```markdown +# .copilot-memory/conventions.md +- Use DefaultAzureCredential for all Azure auth +- Parameter is semantic_configuration_name=, not semantic_configuration= +``` + +## Compaction + +Learnings decay over time: +- Entries older than 60 days with low hit count are pruned +- High-value learnings (frequently referenced) persist indefinitely +- Tool logs are pruned after 7 days + +This prevents unbounded growth while preserving what matters. + +## Best Practices + +1. **One step to install** — if it takes more than `cp -r`, it won't get adopted +2. **Scope correctly** — global for tool patterns, local for project conventions +3. **Be specific** — `"Use semantic_configuration_name="` beats `"use the right parameter"` +4. **Let it compound** — small improvements per session create exponential gains over weeks diff --git a/.agents/skills/frontend-design-review/SKILL.md b/.agents/skills/frontend-design-review/SKILL.md new file mode 100644 index 00000000000..6fd8fe37802 --- /dev/null +++ b/.agents/skills/frontend-design-review/SKILL.md @@ -0,0 +1,138 @@ +--- +name: frontend-design-review +description: > + Review and create distinctive, production-grade frontend interfaces with high design quality and design system compliance. + Evaluates using three pillars: frictionless insight-to-action, quality craft, and trustworthy building. + USE FOR: PR reviews, design reviews, accessibility audits, design system compliance checks, creative frontend design, + UI code review, component reviews, responsive design checks, theme testing, and creating memorable UI. + DO NOT USE FOR: Backend API reviews, database schema reviews, infrastructure or DevOps work, pure business logic + without UI, or non-frontend code. +acknowledgments: | + Design review principles and quality pillar framework created by @Quirinevwm (https://github.com/Quirinevwm). + Creative frontend guidance inspired by Anthropic's frontend-design skill + (https://github.com/anthropics/skills/tree/main/skills/frontend-design). Licensed under respective terms. +--- + +# Frontend Design Review + +Review UI implementations against design quality standards and your design system **OR** create distinctive, production-grade frontend interfaces from scratch. + +## Two Modes + +### Mode 1: Design Review +Evaluate existing UI for design system compliance, three quality pillars (Frictionless, Quality Craft, Trustworthy), accessibility, and code quality. + +### Mode 2: Creative Frontend Design +Create distinctive interfaces that avoid generic "AI slop" aesthetics, have clear conceptual direction, and execute with precision. + +--- + +## Creative Frontend Design + +Before coding, commit to an aesthetic direction: +- **Purpose**: What problem does this solve? Who uses it? +- **Tone**: minimal, maximalist, retro-futuristic, organic, luxury, playful, editorial, brutalist, art deco, soft/pastel, industrial, etc. +- **Constraints**: Framework, performance, accessibility requirements. +- **Differentiation**: What makes this distinctive and context-appropriate? + +### Aesthetics Guidelines + +- **Typography**: Distinctive fonts that elevate aesthetics. Pair a display font with a refined body font. Avoid Inter, Roboto, Arial, Space Grotesk. +- **Color & Theme**: Cohesive palette with CSS variables. Dominant colors + sharp accents > timid, evenly-distributed palettes. +- **Motion**: CSS-only preferred. One well-orchestrated page load with staggered reveals > scattered micro-interactions. +- **Spatial Composition**: Asymmetry, overlap, diagonal flow, grid-breaking elements, generous negative space OR controlled density. +- **Backgrounds**: Gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, grain overlays. + +**AVOID**: Overused fonts, cliched color schemes, predictable layouts, cookie-cutter design without context-specific character. + +Match implementation complexity to vision. Maximalist = elaborate code. Minimalist = restraint and precision. + +--- + +## Design Review + +### Design System Workflow + +**Before implementing:** +1. Review component in your Storybook / component library for API and usage +2. Use Figma Dev Mode to get exact specs (spacing, tokens, properties) +3. Implement using design system components + design tokens + +**During review:** +1. Compare implementation to Figma design +2. Verify design tokens are used (not hardcoded values) +3. Check all variants/states are implemented correctly +4. Flag deviations (needs design approval) + +**If component doesn't exist:** +1. Check if existing component can be adapted +2. Reach out to design for new component creation +3. Document exception and rationale in code + +### Review Process + +1. Identify user task +2. Check design system for matching patterns +3. Evaluate aesthetic direction +4. Identify scope (component, feature, or flow) +5. Evaluate each pillar +6. Score and prioritize issues (blocking/major/minor) +7. Provide recommendations with design system examples + +### Core Principles + +- **Task completion**: Minimum clicks. Every screen answers "What can I do?" and "What happens next?" +- **Action hierarchy**: 1-2 primary actions per view. Progressive disclosure for secondary. +- **Onboarding**: Explain features on introduction. Smart defaults over configuration. +- **Navigation**: Clear entry/exit points. Back/cancel always available. Breadcrumbs for deep flows. + +--- + +## Quality Pillars + +### 1. Frictionless Insight to Action + +**Evaluate:** Task completable in ≤3 interactions? Primary action obvious and singular? + +**Red flags:** Excessive clicks, multiple competing primary buttons, buried actions, dead ends. + +### 2. Quality is Craft + +**Evaluate:** +- Design system compliance: matches Figma specs, uses design tokens +- Aesthetic direction: distinctive typography, cohesive colors, intentional motion +- Accessibility: Grade C minimum (WCAG 2.1 A), Grade B ideal (WCAG 2.1 AA) + +**Red flags:** Generic AI aesthetics, hardcoded values, implementation doesn't match Figma, broken reflow, missing focus indicators. + +### 3. Trustworthy Building + +**Evaluate:** +- AI transparency: disclaimer on AI-generated content +- Error transparency: actionable error messages + +**Red flags:** Missing AI disclaimers, opaque errors without guidance. + +--- + +## Review Output Format + +See [references/review-output-format.md](references/review-output-format.md) for the full review template. + +## Review Type Modifiers + +See [references/review-type-modifiers.md](references/review-type-modifiers.md) for context-specific review focus areas (PR, Creative, Design, Accessibility). + +## Quick Checklist + +See [references/quick-checklist.md](references/quick-checklist.md) for the pre-approval checklist covering design system compliance, aesthetic quality, frictionless, quality craft, and trustworthy pillars. + +## Pattern Examples + +See [references/pattern-examples.md](references/pattern-examples.md) for good/bad examples of creative frontend and design system review work. + +--- + +## Acknowledgments + +Creative frontend principles inspired by [Anthropic's frontend-design skill](https://github.com/anthropics/skills/tree/main/skills/frontend-design). Design review principles and quality pillar framework created by [@Quirinevwm](https://github.com/Quirinevwm) for systematic UI evaluation. diff --git a/.agents/skills/frontend-design-review/references/pattern-examples.md b/.agents/skills/frontend-design-review/references/pattern-examples.md new file mode 100644 index 00000000000..e2133bbe0cd --- /dev/null +++ b/.agents/skills/frontend-design-review/references/pattern-examples.md @@ -0,0 +1,21 @@ +# Pattern Examples + +## Creative Frontend (New Interfaces) + +### Good: Clear Aesthetic Direction +- Landing page with brutalist aesthetic: Raw typography (Neue Haas Grotesk), stark black and white, asymmetric layouts +- Dashboard with organic theme: Rounded forms, earth tones, flowing animations, textured backgrounds + +### Bad: Generic AI Aesthetic +- Overused fonts, cliched color schemes, centered content, generic card layouts + +## Design System Review (Existing Work) + +### Good: Frictionless +- Single primary button, clear task completion path + +### Good: Quality Craft +- Uses design system with tokens, distinctive typography, keyboard accessible, tested in themes + +### Bad: Quality Craft +- Hardcoded values, generic overused fonts, poor contrast in dark mode diff --git a/.agents/skills/frontend-design-review/references/quick-checklist.md b/.agents/skills/frontend-design-review/references/quick-checklist.md new file mode 100644 index 00000000000..7c29054b2f3 --- /dev/null +++ b/.agents/skills/frontend-design-review/references/quick-checklist.md @@ -0,0 +1,38 @@ +# Quick Checklist + +Before approving any UI work: + +## Design System Compliance +- [ ] Component verified in your Figma Design System +- [ ] Component implementation checked in your Component Library +- [ ] Figma Dev Mode specs followed (spacing, tokens, typography) +- [ ] Design tokens used (no hardcoded hex colors or pixel values) +- [ ] Token imports verified in code +- [ ] All variants/states implemented as designed in Figma +- [ ] Spacing measurements match Figma Dev Mode exactly +- [ ] Deviations documented with design approval + +## Aesthetic Quality (especially for new designs) +- [ ] Clear conceptual direction (not generic overused fonts and cliched schemes) +- [ ] Distinctive typography (avoid overused fonts) +- [ ] Cohesive color palette with CSS variables +- [ ] Intentional motion (staggered reveals, hover states) +- [ ] Visual interest through composition (asymmetry, overlap, grid-breaking) +- [ ] Atmosphere through backgrounds (gradients, textures, patterns) +- [ ] Implementation complexity matches vision + +## Frictionless +- [ ] Core task completable efficiently (≤3 interactions) +- [ ] Single clear primary action per view + +## Quality Craft +- [ ] Uses design system components (verified in Figma) +- [ ] Design tokens used (no hardcoded values) +- [ ] Distinctive aesthetic (not generic overused fonts/cliched schemes) +- [ ] Accessible (Grade C minimum, Grade B ideal) +- [ ] Keyboard navigation complete +- [ ] Tested in light/dark/high contrast modes + +## Trustworthy +- [ ] AI-generated content has disclaimer +- [ ] Error messages are actionable diff --git a/.agents/skills/frontend-design-review/references/review-output-format.md b/.agents/skills/frontend-design-review/references/review-output-format.md new file mode 100644 index 00000000000..065f3cef3e4 --- /dev/null +++ b/.agents/skills/frontend-design-review/references/review-output-format.md @@ -0,0 +1,68 @@ +# Review Output Format + +``` +## Frontend Design Review: [Component/Feature Name] + +### Context +- **Purpose**: What problem does this solve? Who uses it? +- **Aesthetic Direction**: [If new design: describe the bold conceptual direction] +- **User Task**: What is the user trying to accomplish? + +### Summary +[Pass/Needs Work/Blocked] - [One-line assessment] + +### Design System Compliance (if applicable) +- [ ] Component exists in [Your Figma Design System] +- [ ] Component usage verified in [Your Component Library] +- [ ] Implementation matches Figma specs (spacing, colors, typography) +- [ ] Uses design tokens (not hardcoded values) - verified in code +- [ ] All variants match design system options +- [ ] Spacing verified against Figma Dev Mode +- [ ] Documented exception if deviating from design system + +### Aesthetic Quality (especially for new designs) +- [ ] Clear conceptual direction (not generic AI aesthetic) +- [ ] Distinctive typography choices +- [ ] Cohesive color palette with CSS variables +- [ ] Intentional motion and micro-interactions +- [ ] Spatial composition creates visual interest +- [ ] Backgrounds and visual details add atmosphere + +### Pillar Assessment + +| Pillar | Status | Notes | +|--------|--------|-------| +| Frictionless | 🟢/🟠/⚫ | Task completion efficient, primary action clear | +| Quality Craft | 🟢/🟠/⚫ | Design system compliant, aesthetic distinctive, accessible | +| Trustworthy | 🟢/🟠/⚫ | AI disclaimers present, errors actionable | + +**Legend:** 🟢 Pass | 🟠 Needs attention | ⚫ Blocking issue + +### Design Critique +**Verdict:** [Pass / Needs work / Reach out to design for more support] + +**Rationale:** [Brief explanation based on pillar assessment, design system compliance, and aesthetic direction] + +**Criteria:** +- **Pass**: All pillars 🟢 or minor 🟠 that don't block user tasks, design system compliant, clear aesthetic direction +- **Needs work**: Multiple 🟠 or any critical workflow issues, design system deviations, or generic aesthetic choices +- **Reach out to design for more support**: Any ⚫ blocking issues, fundamental pattern problems, major design system violations, or need for aesthetic direction + +### Issues + +**Blocking (must fix before merge):** +1. [Pillar/Design System/Aesthetic] Issue description + recommendation with link + +**Major (should fix):** +1. [Pillar/Design System/Aesthetic] Issue description + pattern suggestion with reference + +**Minor (consider for refinement):** +1. [Pillar/Design System/Aesthetic] Issue description + optional improvement + +### Recommendations +- [Design system component to use with link] +- [Specific code change with design token reference] +- [Typography recommendation for better aesthetic direction] +- [Motion/animation suggestion] +- [Link to design system in Figma] +``` diff --git a/.agents/skills/frontend-design-review/references/review-type-modifiers.md b/.agents/skills/frontend-design-review/references/review-type-modifiers.md new file mode 100644 index 00000000000..d3f94c042bd --- /dev/null +++ b/.agents/skills/frontend-design-review/references/review-type-modifiers.md @@ -0,0 +1,31 @@ +# Review Type Modifiers + +Adjust focus based on review context: + +## PR Review +- **Focus**: Code implementation, design system component usage, design token usage, accessibility in code +- **Check**: Proper imports, design tokens used (not hardcoded), ARIA attributes present +- **Verify**: Component matches Figma specs using Dev Mode + +## Creative Frontend Review +- **Focus**: Aesthetic direction, typography choices, visual distinctiveness, motion design +- **Check**: Clear conceptual intent, avoiding generic AI patterns, cohesive execution +- **Verify**: Implementation complexity matches vision (maximalist needs elaborate code, minimalist needs precision) + +## Design Review +- **Focus**: User flows, interaction patterns, visual hierarchy, navigation, design system alignment +- **Check**: Task completion path, action hierarchy, progressive disclosure +- **Verify**: All components exist in design system or have documented exceptions + +## Accessibility Audit +- **Focus**: Deep dive Quality Craft pillar +- **Check**: Keyboard testing, screen reader testing, contrast ratios, ARIA patterns +- **Test with**: Screen readers (NVDA, JAWS, Narrator), keyboard only, 200% zoom +- **Verify**: Design system accessibility features are properly implemented + +## Design System Compliance Audit +- **Focus**: Deep dive design system usage +- **Check**: All components match Figma specs, design tokens used throughout, no hardcoded values +- **Test**: Compare implementation side-by-side with Figma using Dev Mode +- **Verify**: Component variants, spacing, colors, typography all match design system +- **Document**: Any deviations with rationale and plan to align diff --git a/.agents/skills/microsoft-docs/SKILL.md b/.agents/skills/microsoft-docs/SKILL.md new file mode 100644 index 00000000000..a5129731352 --- /dev/null +++ b/.agents/skills/microsoft-docs/SKILL.md @@ -0,0 +1,77 @@ +--- +name: microsoft-docs +description: Understand Microsoft technologies by querying official documentation. Use whenever the user asks how something works, wants tutorials, needs configuration options, limits, quotas, or best practices for any Microsoft technology (Azure, .NET, M365, Windows, Power Platform, etc.)—even if they don't mention "docs." If the question is about understanding a concept rather than writing code, this is the right skill. +context: fork +compatibility: Primarily uses the Microsoft Learn MCP Server (https://learn.microsoft.com/api/mcp); if that is unavailable, fall back to the mslearn CLI (`npx @microsoft/learn-cli`). +--- + +# Microsoft Docs + +## Tools + +| Tool | Use For | +|------|---------| +| `microsoft_docs_search` | Find documentation—concepts, guides, tutorials, configuration | +| `microsoft_docs_fetch` | Get full page content (when search excerpts aren't enough) | + +## When to Use + +- **Understanding concepts** — "How does Cosmos DB partitioning work?" +- **Learning a service** — "Azure Functions overview", "Container Apps architecture" +- **Finding tutorials** — "quickstart", "getting started", "step-by-step" +- **Configuration options** — "App Service configuration settings" +- **Limits & quotas** — "Azure OpenAI rate limits", "Service Bus quotas" +- **Best practices** — "Azure security best practices" + +## Query Effectiveness + +Good queries are specific: + +``` +# ❌ Too broad +"Azure Functions" + +# ✅ Specific +"Azure Functions Python v2 programming model" +"Cosmos DB partition key design best practices" +"Container Apps scaling rules KEDA" +``` + +Include context: +- **Version** when relevant (`.NET 8`, `EF Core 8`) +- **Task intent** (`quickstart`, `tutorial`, `overview`, `limits`) +- **Platform** for multi-platform docs (`Linux`, `Windows`) + +## When to Fetch Full Page + +Fetch after search when: +- **Tutorials** — need complete step-by-step instructions +- **Configuration guides** — need all options listed +- **Deep dives** — user wants comprehensive coverage +- **Search excerpt is cut off** — full context needed + +## Why Use This + +- **Accuracy** — live docs, not training data that may be outdated +- **Completeness** — tutorials have all steps, not fragments +- **Authority** — official Microsoft documentation + +## CLI Alternative + +If the Learn MCP server is not available, use the `mslearn` CLI from the command line instead: + +```sh +# Run directly (no install needed) +npx @microsoft/learn-cli search "azure functions timeout" + +# Or install globally, then run +npm install -g @microsoft/learn-cli +mslearn search "azure functions timeout" +``` + +| MCP Tool | CLI Command | +|----------|-------------| +| `microsoft_docs_search(query: "...")` | `mslearn search "..."` | +| `microsoft_docs_fetch(url: "...")` | `mslearn fetch "..."` | + +The `fetch` command also supports `--section ` to extract a single section and `--max-chars ` to truncate output. diff --git a/.agents/skills/skill-creator/SKILL.md b/.agents/skills/skill-creator/SKILL.md new file mode 100644 index 00000000000..1f27c72ac98 --- /dev/null +++ b/.agents/skills/skill-creator/SKILL.md @@ -0,0 +1,613 @@ +--- +name: skill-creator +description: Guide for creating effective skills for AI coding agents working with Azure SDKs and Microsoft Foundry services. Use when creating new skills or updating existing skills. +--- + +# Skill Creator + +Guide for creating skills that extend AI agent capabilities, with emphasis on Azure SDKs and Microsoft Foundry. + +> **Required Context:** When creating SDK or API skills, users MUST provide the SDK package name, documentation URL, or repository reference for the skill to be based on. + +## About Skills + +Skills are modular knowledge packages that transform general-purpose agents into specialized experts: + +1. **Procedural knowledge** — Multi-step workflows for specific domains +2. **SDK expertise** — API patterns, authentication, error handling for Azure services +3. **Domain context** — Schemas, business logic, company-specific patterns +4. **Bundled resources** — Scripts, references, templates for complex tasks + +--- + +## Core Principles + +### 1. Concise is Key + +The context window is a shared resource. Challenge each piece: "Does this justify its token cost?" + +**Default assumption: Agents are already capable.** Only add what they don't already know. + +### 2. Fresh Documentation First + +**Azure SDKs change constantly.** Skills should instruct agents to verify documentation: + +```markdown +## Before Implementation + +Search `microsoft-docs` MCP for current API patterns: +- Query: "[SDK name] [operation] python" +- Verify: Parameters match your installed SDK version +``` + +### 3. Degrees of Freedom + +Match specificity to task fragility: + +| Freedom | When | Example | +|---------|------|---------| +| **High** | Multiple valid approaches | Text guidelines | +| **Medium** | Preferred pattern with variation | Pseudocode | +| **Low** | Must be exact | Specific scripts | + +### 4. Progressive Disclosure + +Skills load in three levels: + +1. **Metadata** (~100 words) — Always in context +2. **SKILL.md body** (<5k words) — When skill triggers +3. **References** (unlimited) — As needed + +**Keep SKILL.md under 500 lines.** Split into reference files when approaching this limit. + +--- + +## Skill Structure + +``` +skill-name/ +├── SKILL.md (required) +│ ├── YAML frontmatter (name, description) +│ └── Markdown instructions +└── Bundled Resources (optional) + ├── scripts/ — Executable code + ├── references/ — Documentation loaded as needed + └── assets/ — Output resources (templates, images) +``` + +### SKILL.md + +- **Frontmatter**: `name` and `description`. The description is the trigger mechanism. +- **Body**: Instructions loaded only after triggering. + +### Bundled Resources + +| Type | Purpose | When to Include | +|------|---------|-----------------| +| `scripts/` | Deterministic operations | Same code rewritten repeatedly | +| `references/` | Detailed patterns | API docs, schemas, detailed guides | +| `assets/` | Output resources | Templates, images, boilerplate | + +**Don't include**: README.md, CHANGELOG.md, installation guides. + +--- + +## Creating Azure SDK Skills + +When creating skills for Azure SDKs, follow these patterns consistently. + +### Skill Section Order + +Follow this structure (based on existing Azure SDK skills): + +1. **Title** — `# SDK Name` +2. **Installation** — `pip install`, `npm install`, etc. +3. **Environment Variables** — Required configuration +4. **Authentication** — Always `DefaultAzureCredential` +5. **Core Workflow** — Minimal viable example +6. **Feature Tables** — Clients, methods, tools +7. **Best Practices** — Numbered list +8. **Reference Links** — Table linking to `/references/*.md` + +### Authentication Pattern (All Languages) + +Always use `DefaultAzureCredential`: + +```python +# Python +from azure.identity import DefaultAzureCredential +credential = DefaultAzureCredential() +client = ServiceClient(endpoint, credential) +``` + +```csharp +// C# +var credential = new DefaultAzureCredential(); +var client = new ServiceClient(new Uri(endpoint), credential); +``` + +```java +// Java +TokenCredential credential = new DefaultAzureCredentialBuilder().build(); +ServiceClient client = new ServiceClientBuilder() + .endpoint(endpoint) + .credential(credential) + .buildClient(); +``` + +```typescript +// TypeScript +import { DefaultAzureCredential } from "@azure/identity"; +const credential = new DefaultAzureCredential(); +const client = new ServiceClient(endpoint, credential); +``` + +**Never hardcode credentials. Use environment variables.** + +### Standard Verb Patterns + +Azure SDKs use consistent verbs across all languages: + +| Verb | Behavior | +|------|----------| +| `create` | Create new; fail if exists | +| `upsert` | Create or update | +| `get` | Retrieve; error if missing | +| `list` | Return collection | +| `delete` | Succeed even if missing | +| `begin` | Start long-running operation | + +### Language-Specific Patterns + +See `references/azure-sdk-patterns.md` for detailed patterns including: + +- **Python**: `ItemPaged`, `LROPoller`, context managers, Sphinx docstrings +- **.NET**: `Response`, `Pageable`, `Operation`, mocking support +- **Java**: Builder pattern, `PagedIterable`/`PagedFlux`, Reactor types +- **TypeScript**: `PagedAsyncIterableIterator`, `AbortSignal`, browser considerations + +### Example: Azure SDK Skill Structure + +```markdown +--- +name: skill-creator +description: | + Azure AI Example SDK for Python. Use for [specific service features]. + Triggers: "example service", "create example", "list examples". +--- + +# Azure AI Example SDK + +## Installation + +\`\`\`bash +pip install azure-ai-example +\`\`\` + +## Environment Variables + +\`\`\`bash +AZURE_EXAMPLE_ENDPOINT=https://.example.azure.com +\`\`\` + +## Authentication + +\`\`\`python +from azure.identity import DefaultAzureCredential +from azure.ai.example import ExampleClient + +credential = DefaultAzureCredential() +client = ExampleClient( + endpoint=os.environ["AZURE_EXAMPLE_ENDPOINT"], + credential=credential +) +\`\`\` + +## Core Workflow + +\`\`\`python +# Create +item = client.create_item(name="example", data={...}) + +# List (pagination handled automatically) +for item in client.list_items(): + print(item.name) + +# Long-running operation +poller = client.begin_process(item_id) +result = poller.result() + +# Cleanup +client.delete_item(item_id) +\`\`\` + +## Reference Files + +| File | Contents | +|------|----------| +| [references/tools.md](references/tools.md) | Tool integrations | +| [references/streaming.md](references/streaming.md) | Event streaming patterns | +``` + +--- + +## Skill Creation Process + +1. **Gather SDK Context** — User provides SDK/API reference (REQUIRED) +2. **Understand** — Research SDK patterns from official docs +3. **Plan** — Identify reusable resources and product area category +4. **Create** — Write SKILL.md in `.github/skills//` +5. **Categorize** — Create symlink in `skills///` +6. **Test** — Create acceptance criteria and test scenarios +7. **Document** — Update README.md skill catalog +8. **Iterate** — Refine based on real usage + +### Step 1: Gather SDK Context (REQUIRED) + +**Before creating any SDK skill, the user MUST provide:** + +| Required | Example | Purpose | +|----------|---------|---------| +| **SDK Package** | `azure-ai-agents`, `Azure.AI.OpenAI` | Identifies the exact SDK | +| **Documentation URL** | `https://learn.microsoft.com/en-us/azure/ai-services/...` | Primary source of truth | +| **Repository** (optional) | `Azure/azure-sdk-for-python` | For code patterns | + +**Prompt the user if not provided:** +``` +To create this skill, I need: +1. The SDK package name (e.g., azure-ai-projects) +2. The Microsoft Learn documentation URL or GitHub repo +3. The target language (py/dotnet/ts/java) +``` + +**Search official docs first:** +```bash +# Use microsoft-docs MCP to get current API patterns +# Query: "[SDK name] [operation] [language]" +# Verify: Parameters match the latest SDK version +``` + +### Step 2: Understand the Skill + +Gather concrete examples: + +- "What SDK operations should this skill cover?" +- "What triggers should activate this skill?" +- "What errors do developers commonly encounter?" + +| Example Task | Reusable Resource | +|--------------|-------------------| +| Same auth code each time | Code example in SKILL.md | +| Complex streaming patterns | `references/streaming.md` | +| Tool configurations | `references/tools.md` | +| Error handling patterns | `references/error-handling.md` | + +### Step 3: Plan Product Area Category + +Skills are organized by **language** and **product area** in the `skills/` directory via symlinks. + +**Product Area Categories:** + +| Category | Description | Examples | +|----------|-------------|----------| +| `foundry` | AI Foundry, agents, projects, inference | `azure-ai-agents-py`, `azure-ai-projects-py` | +| `data` | Storage, Cosmos DB, Tables, Data Lake | `azure-cosmos-py`, `azure-storage-blob-py` | +| `messaging` | Event Hubs, Service Bus, Event Grid | `azure-eventhub-py`, `azure-servicebus-py` | +| `monitoring` | OpenTelemetry, App Insights, Query | `azure-monitor-opentelemetry-py` | +| `identity` | Authentication, DefaultAzureCredential | `azure-identity-py` | +| `security` | Key Vault, secrets, keys, certificates | `azure-keyvault-py` | +| `integration` | API Management, App Configuration | `azure-appconfiguration-py` | +| `compute` | Batch, ML compute | `azure-compute-batch-java` | +| `container` | Container Registry, ACR | `azure-containerregistry-py` | + +**Determine the category** based on: +1. Azure service family (Storage → `data`, Event Hubs → `messaging`) +2. Primary use case (AI agents → `foundry`) +3. Existing skills in the same service area + +### Step 4: Create the Skill + +**Location:** `.github/skills//SKILL.md` + +**Naming convention:** +- `azure---` +- Examples: `azure-ai-agents-py`, `azure-cosmos-java`, `azure-storage-blob-ts` + +**For Azure SDK skills:** + +1. Search `microsoft-docs` MCP for current API patterns +2. Verify against installed SDK version +3. Follow the section order above +4. Include cleanup code in examples +5. Add feature comparison tables + +**Write bundled resources first**, then SKILL.md. + +**Frontmatter:** + +```yaml +--- +name: skill-name-py +description: | + Azure Service SDK for Python. Use for [specific features]. + Triggers: "service name", "create resource", "specific operation". +--- +``` + +### Step 5: Categorize with Symlinks + +After creating the skill in `.github/skills/`, create a symlink in the appropriate category: + +```bash +# Pattern: skills/// -> ../../../.github/skills/ + +# Example for azure-ai-agents-py in python/foundry: +cd skills/python/foundry +ln -s ../../../.github/skills/azure-ai-agents-py agents + +# Example for azure-cosmos-db-py in python/data: +cd skills/python/data +ln -s ../../../.github/skills/azure-cosmos-db-py cosmos-db +``` + +**Symlink naming:** +- Use short, descriptive names (e.g., `agents`, `cosmos`, `blob`) +- Remove the `azure-` prefix and language suffix +- Match existing patterns in the category + +**Verify the symlink:** +```bash +ls -la skills/python/foundry/agents +# Should show: agents -> ../../../.github/skills/azure-ai-agents-py +``` + +### Step 6: Create Tests + +**Every skill MUST have acceptance criteria and test scenarios.** + +#### 6.1 Create Acceptance Criteria + +**Location:** `.github/skills//references/acceptance-criteria.md` + +**Source materials** (in priority order): +1. Official Microsoft Learn docs (via `microsoft-docs` MCP) +2. SDK source code from the repository +3. Existing reference files in the skill + +**Format:** +```markdown +# Acceptance Criteria: + +**SDK**: `package-name` +**Repository**: https://github.com/Azure/azure-sdk-for- +**Purpose**: Skill testing acceptance criteria + +--- + +## 1. Correct Import Patterns + +### 1.1 Client Imports + +#### ✅ CORRECT: Main Client +\`\`\`python +from azure.ai.mymodule import MyClient +from azure.identity import DefaultAzureCredential +\`\`\` + +#### ❌ INCORRECT: Wrong Module Path +\`\`\`python +from azure.ai.mymodule.models import MyClient # Wrong - Client is not in models +\`\`\` + +## 2. Authentication Patterns + +#### ✅ CORRECT: DefaultAzureCredential +\`\`\`python +credential = DefaultAzureCredential() +client = MyClient(endpoint, credential) +\`\`\` + +#### ❌ INCORRECT: Hardcoded Credentials +\`\`\`python +client = MyClient(endpoint, api_key="hardcoded") # Security risk +\`\`\` +``` + +**Critical patterns to document:** +- Import paths (these vary significantly between Azure SDKs) +- Authentication patterns +- Client initialization +- Async variants (`.aio` modules) +- Common anti-patterns + +#### 6.2 Create Test Scenarios + +**Location:** `tests/scenarios//scenarios.yaml` + +```yaml +config: + model: gpt-4 + max_tokens: 2000 + temperature: 0.3 + +scenarios: + - name: basic_client_creation + prompt: | + Create a basic example using the Azure SDK. + Include proper authentication and client initialization. + expected_patterns: + - "DefaultAzureCredential" + - "MyClient" + forbidden_patterns: + - "api_key=" + - "hardcoded" + tags: + - basic + - authentication + mock_response: | + import os + from azure.identity import DefaultAzureCredential + from azure.ai.mymodule import MyClient + + credential = DefaultAzureCredential() + client = MyClient( + endpoint=os.environ["AZURE_ENDPOINT"], + credential=credential + ) + # ... rest of working example +``` + +**Scenario design principles:** +- Each scenario tests ONE specific pattern or feature +- `expected_patterns` — patterns that MUST appear +- `forbidden_patterns` — common mistakes that must NOT appear +- `mock_response` — complete, working code that passes all checks +- `tags` — for filtering (`basic`, `async`, `streaming`, `tools`) + +#### 6.3 Run Tests + +```bash +cd tests +pnpm install + +# Check skill is discovered +pnpm harness --list + +# Run in mock mode (fast, deterministic) +pnpm harness --mock --verbose + +# Run with Ralph Loop (iterative improvement) +pnpm harness --ralph --mock --max-iterations 5 --threshold 85 +``` + +**Success criteria:** +- All scenarios pass (100% pass rate) +- No false positives (mock responses always pass) +- Patterns catch real mistakes + +### Step 7: Update Documentation + +After creating the skill: + +1. **Update README.md** — Add the skill to the appropriate language section in the Skill Catalog + - Update total skill count (line ~73: `> N skills in...`) + - Update Skill Explorer link count (line ~15: `Browse all N skills`) + - Update language count table (lines ~77-83) + - Update language section count (e.g., `> N skills • suffix: -py`) + - Update category count (e.g., `Foundry & AI (N skills)`) + - Add skill row in alphabetical order within its category + - Update test coverage summary (line ~622: `**N skills with N test scenarios**`) + - Update test coverage table — update skill count, scenario count, and top skills for the language + +2. **Regenerate GitHub Pages data** — Run the extraction script to update the docs site + ```bash + cd docs-site && npx tsx scripts/extract-skills.ts + ``` + This updates `docs-site/src/data/skills.json` which feeds the Astro-based docs site. + Then rebuild the docs site: + ```bash + cd docs-site && npm run build + ``` + This outputs to `docs/` which is served by GitHub Pages. + +3. **Verify AGENTS.md** — Ensure the skill count is accurate + +--- + +## Progressive Disclosure Patterns + +### Pattern 1: High-Level Guide with References + +```markdown +# SDK Name + +## Quick Start +[Minimal example] + +## Advanced Features +- **Streaming**: See [references/streaming.md](references/streaming.md) +- **Tools**: See [references/tools.md](references/tools.md) +``` + +### Pattern 2: Language Variants + +``` +azure-service-skill/ +├── SKILL.md (overview + language selection) +└── references/ + ├── python.md + ├── dotnet.md + ├── java.md + └── typescript.md +``` + +### Pattern 3: Feature Organization + +``` +azure-ai-agents/ +├── SKILL.md (core workflow) +└── references/ + ├── tools.md + ├── streaming.md + ├── async-patterns.md + └── error-handling.md +``` + +--- + +## Design Pattern References + +| Reference | Contents | +|-----------|----------| +| `references/workflows.md` | Sequential and conditional workflows | +| `references/output-patterns.md` | Templates and examples | +| `references/azure-sdk-patterns.md` | Language-specific Azure SDK patterns | + +--- + +## Anti-Patterns + +| Don't | Why | +|-------|-----| +| Create skill without SDK context | Users must provide package name/docs URL | +| Put "when to use" in body | Body loads AFTER triggering | +| Hardcode credentials | Security risk | +| Skip authentication section | Agents will improvise poorly | +| Use outdated SDK patterns | APIs change; search docs first | +| Include README.md | Agents don't need meta-docs | +| Deeply nest references | Keep one level deep | +| Skip acceptance criteria | Skills without tests can't be validated | +| Skip symlink categorization | Skills won't be discoverable by category | +| Use wrong import paths | Azure SDKs have specific module structures | + +--- + +## Checklist + +Before completing a skill: + +**Prerequisites:** +- [ ] User provided SDK package name or documentation URL +- [ ] Verified SDK patterns via `microsoft-docs` MCP + +**Skill Creation:** +- [ ] Description includes what AND when (trigger phrases) +- [ ] SKILL.md under 500 lines +- [ ] Authentication uses `DefaultAzureCredential` +- [ ] Includes cleanup/delete in examples +- [ ] References organized by feature + +**Categorization:** +- [ ] Skill created in `.github/skills//` +- [ ] Symlink created in `skills///` +- [ ] Symlink points to `../../../.github/skills/` + +**Testing:** +- [ ] `references/acceptance-criteria.md` created with correct/incorrect patterns +- [ ] `tests/scenarios//scenarios.yaml` created +- [ ] All scenarios pass (`pnpm harness --mock`) +- [ ] Import paths documented precisely + +**Documentation:** +- [ ] README.md skill catalog updated +- [ ] Instructs to search `microsoft-docs` MCP for current APIs diff --git a/.agents/skills/skill-creator/references/acceptance-criteria.md b/.agents/skills/skill-creator/references/acceptance-criteria.md new file mode 100644 index 00000000000..d692ece30f3 --- /dev/null +++ b/.agents/skills/skill-creator/references/acceptance-criteria.md @@ -0,0 +1,499 @@ +# Skill Creator Acceptance Criteria + +**Skill**: `skill-creator` +**Purpose**: Guide for creating effective skills for AI coding agents +**Focus**: SKILL.md format, YAML frontmatter, bundled resources, Azure SDK patterns + +--- + +## 1. SKILL.md Structure + +### 1.1 ✅ CORRECT: Complete SKILL.md with Frontmatter + +```markdown +--- +name: azure-example-py +description: | + Azure Example SDK for Python. Use for creating and managing examples. + Triggers: "create example", "list examples", "azure example sdk". +--- + +# Azure Example SDK + +## Installation + +\`\`\`bash +pip install azure-example +\`\`\` + +## Environment Variables + +\`\`\`bash +AZURE_EXAMPLE_ENDPOINT=https://.example.azure.com +\`\`\` + +## Authentication + +\`\`\`python +from azure.identity import DefaultAzureCredential +from azure.example import ExampleClient + +credential = DefaultAzureCredential() +client = ExampleClient( + endpoint=os.environ["AZURE_EXAMPLE_ENDPOINT"], + credential=credential +) +\`\`\` + +## Core Workflow + +\`\`\`python +# Create +item = client.create_item(name="example") + +# List +for item in client.list_items(): + print(item.name) + +# Delete +client.delete_item(item_id) +\`\`\` + +## Reference Files + +| File | Contents | +|------|----------| +| [references/tools.md](references/tools.md) | Tool integrations | +``` + +### 1.2 ❌ INCORRECT: Missing Frontmatter + +```markdown +# My Skill + +This skill does stuff. +``` + +### 1.3 ❌ INCORRECT: Triggers in Body Instead of Description + +```markdown +--- +name: my-skill +description: A skill for doing things. +--- + +# My Skill + +**When to use:** Use this skill when you need to do X, Y, or Z. +``` + +--- + +## 2. YAML Frontmatter + +### 2.1 ✅ CORRECT: Complete Frontmatter + +```yaml +--- +name: azure-ai-agents-py +description: | + Azure AI Agents SDK for Python. Use for creating agents with tools, + managing threads, streaming responses, and function calling. + Triggers: "create agent", "azure agents", "function calling", "streaming agents". +--- +``` + +### 2.2 ✅ CORRECT: Single-Line Description + +```yaml +--- +name: pydantic-models-py +description: Pydantic v2 model patterns for FastAPI. Use for request/response models, validation, and serialization. +--- +``` + +### 2.3 ❌ INCORRECT: Missing Name + +```yaml +--- +description: A skill for something. +--- +``` + +### 2.4 ❌ INCORRECT: Missing Description + +```yaml +--- +name: my-skill +--- +``` + +### 2.5 ❌ INCORRECT: No Trigger Phrases + +```yaml +--- +name: azure-search-py +description: Azure Search SDK for Python. +--- +``` + +--- + +## 3. Skill Naming Convention + +### 3.1 ✅ CORRECT: Language-Suffixed Names + +| Pattern | Examples | +|---------|----------| +| Python | `azure-ai-agents-py`, `fastapi-router-py` | +| .NET | `azure-ai-agents-dotnet`, `azure-openai-dotnet` | +| TypeScript | `azure-ai-agents-ts`, `react-flow-node-ts` | +| Java | `azure-ai-agents-java`, `azure-cosmos-java` | +| Cross-language | `mcp-builder`, `skill-creator`, `copilot-sdk` | + +### 3.2 ❌ INCORRECT: Wrong Naming Patterns + +``` +azure-ai-agents # Missing language suffix +azure_ai_agents_py # Uses underscores +AzureAiAgentsPy # Uses camelCase +``` + +--- + +## 4. Description Best Practices + +### 4.1 ✅ CORRECT: Description with What + When + Triggers + +```yaml +description: | + Azure AI Search SDK for Python. Use for vector search, hybrid search, + index management, and document operations. + Triggers: "semantic search", "vector index", "azure search", "hybrid search". +``` + +### 4.2 ✅ CORRECT: Concise Single-Line + +```yaml +description: FastAPI router patterns with CRUD, auth, and response models. Triggers on "create router", "FastAPI endpoint", "API route". +``` + +### 4.3 ❌ INCORRECT: Too Verbose + +```yaml +description: | + This is a comprehensive skill that covers all aspects of the Azure AI Search + SDK for Python. It includes detailed information about how to create indexes, + manage documents, perform searches, and configure analyzers. The skill also + covers advanced topics like vector search with embeddings, hybrid search + combining keyword and vector approaches, and semantic ranking. Additionally, + it provides guidance on authentication patterns using DefaultAzureCredential + and environment variable configuration. +``` + +### 4.4 ❌ INCORRECT: No Usage Context + +```yaml +description: Azure AI Search SDK for Python. +``` + +--- + +## 5. Skill Body Structure + +### 5.1 ✅ CORRECT: Section Order for Azure SDK Skills + +```markdown +# SDK Name + +## Installation +[pip/npm/dotnet install commands] + +## Environment Variables +[Required configuration] + +## Authentication +[DefaultAzureCredential pattern] + +## Core Workflow +[Minimal working example] + +## Feature Tables +[Clients, methods, options] + +## Best Practices +[Numbered list] + +## Reference Files +[Table linking to /references/*.md] +``` + +### 5.2 ✅ CORRECT: Reference File Table + +```markdown +## Reference Files + +| File | Contents | +|------|----------| +| [references/tools.md](references/tools.md) | Tool configurations | +| [references/streaming.md](references/streaming.md) | Event streaming patterns | +| [references/async.md](references/async.md) | Async/await patterns | +``` + +### 5.3 ❌ INCORRECT: Missing Authentication Section + +```markdown +# Azure Example SDK + +## Installation +\`\`\`bash +pip install azure-example +\`\`\` + +## Core Workflow +\`\`\`python +client = ExampleClient() # How to authenticate? +\`\`\` +``` + +--- + +## 6. Authentication Patterns + +### 6.1 ✅ CORRECT: DefaultAzureCredential (Python) + +```python +from azure.identity import DefaultAzureCredential +from azure.example import ExampleClient + +credential = DefaultAzureCredential() +client = ExampleClient( + endpoint=os.environ["AZURE_EXAMPLE_ENDPOINT"], + credential=credential +) +``` + +### 6.2 ✅ CORRECT: DefaultAzureCredential (.NET) + +```csharp +using Azure.Identity; +using Azure.Example; + +var credential = new DefaultAzureCredential(); +var client = new ExampleClient( + new Uri(Environment.GetEnvironmentVariable("AZURE_EXAMPLE_ENDPOINT")), + credential +); +``` + +### 6.3 ✅ CORRECT: DefaultAzureCredential (TypeScript) + +```typescript +import { DefaultAzureCredential } from "@azure/identity"; +import { ExampleClient } from "@azure/example"; + +const credential = new DefaultAzureCredential(); +const client = new ExampleClient( + process.env.AZURE_EXAMPLE_ENDPOINT!, + credential +); +``` + +### 6.4 ❌ INCORRECT: Hardcoded Credentials + +```python +# WRONG - Never hardcode credentials +client = ExampleClient( + endpoint="https://my-resource.azure.com", + api_key="sk-12345" +) +``` + +--- + +## 7. Directory Structure + +### 7.1 ✅ CORRECT: Standard Skill Structure + +``` +skill-name/ +├── SKILL.md # Required - main skill file +└── references/ # Optional - detailed docs + ├── tools.md + ├── streaming.md + └── acceptance-criteria.md +``` + +### 7.2 ✅ CORRECT: Skill with Scripts + +``` +skill-name/ +├── SKILL.md +├── scripts/ # Executable utilities +│ ├── init_project.py +│ └── validate.py +└── references/ + └── patterns.md +``` + +### 7.3 ❌ INCORRECT: README in Skill Directory + +``` +skill-name/ +├── SKILL.md +├── README.md # Don't include - agents don't need meta-docs +└── CHANGELOG.md # Don't include +``` + +--- + +## 8. Progressive Disclosure + +### 8.1 ✅ CORRECT: Main Content in SKILL.md, Details in References + +**SKILL.md (under 500 lines):** +```markdown +# Azure AI Agents SDK + +## Quick Start +[Minimal example] + +## Tools +For detailed tool configurations, see [references/tools.md](references/tools.md) + +## Streaming +For streaming patterns, see [references/streaming.md](references/streaming.md) +``` + +**references/tools.md:** +```markdown +# Tool Configurations + +## FunctionTool +[Detailed content...] + +## CodeInterpreterTool +[Detailed content...] +``` + +### 8.2 ❌ INCORRECT: Everything in SKILL.md + +```markdown +# Azure AI Agents SDK + +[800+ lines of content including every tool, every pattern, +every edge case, making the skill too large to be useful] +``` + +--- + +## 9. Code Examples + +### 9.1 ✅ CORRECT: Complete, Runnable Examples + +```python +import os +from azure.identity import DefaultAzureCredential +from azure.ai.agents import AgentsClient + +# Initialize +credential = DefaultAzureCredential() +client = AgentsClient( + endpoint=os.environ["PROJECT_ENDPOINT"], + credential=credential +) + +# Create agent +agent = client.create_agent( + model="gpt-4o", + name="my-agent", + instructions="You are a helpful assistant." +) + +# Cleanup +client.delete_agent(agent.id) +``` + +### 9.2 ❌ INCORRECT: Incomplete Examples + +```python +# Create agent +agent = client.create_agent(...) # What parameters? + +# Missing: imports, initialization, cleanup +``` + +--- + +## 10. Content Guidelines + +### 10.1 ✅ CORRECT: Concise, Actionable Content + +```markdown +## Best Practices + +1. Always use `DefaultAzureCredential` for authentication +2. Close clients with context managers: `with client:` +3. Use `create_or_update_*` for idempotent operations +4. Handle pagination with `for item in client.list_items():` +``` + +### 10.2 ❌ INCORRECT: Redundant Explanations + +```markdown +## Best Practices + +The DefaultAzureCredential class is a credential that provides a default +authentication flow for applications that will be deployed to Azure. It +attempts to authenticate using multiple methods in a specific order until +one succeeds. The methods tried are, in order: EnvironmentCredential, +WorkloadIdentityCredential, ManagedIdentityCredential, SharedTokenCacheCredential, +VisualStudioCredential, VisualStudioCodeCredential, AzureCliCredential, and +AzurePowerShellCredential. Each credential type has its own set of requirements... +[continues for 500 more words] +``` + +--- + +## 11. Anti-Patterns Summary + +| Anti-Pattern | Impact | Fix | +|--------------|--------|-----| +| Missing frontmatter | Skill won't load | Add `---` delimited YAML | +| No triggers in description | Won't activate correctly | Add "Use when..." and trigger phrases | +| Hardcoded credentials | Security risk | Use `DefaultAzureCredential` | +| Missing auth section | Agents improvise poorly | Always include auth pattern | +| Too verbose description | Token waste | Keep under 200 words | +| Everything in SKILL.md | Context overflow | Split into references | +| README.md included | Unnecessary | Agents don't need meta-docs | +| Incomplete examples | Agents fill gaps poorly | Show full working code | + +--- + +## 12. Checklist for New Skills + +### Frontmatter +- [ ] Has `name` field with correct format (e.g., `azure-example-py`) +- [ ] Has `description` with what it does AND when to use it +- [ ] Description includes trigger phrases + +### Structure +- [ ] SKILL.md under 500 lines +- [ ] Follows section order: Install → Env → Auth → Core → Features → References +- [ ] Large content split into `references/` files + +### Authentication +- [ ] Uses `DefaultAzureCredential` (never hardcoded) +- [ ] Shows environment variable configuration +- [ ] Includes cleanup/close in examples + +### Quality +- [ ] No README.md, CHANGELOG.md, or meta-docs +- [ ] All code examples are complete and runnable +- [ ] References organized by feature, not by length +- [ ] Instructs to search `microsoft-docs` MCP for current APIs + +### Naming +- [ ] Uses lowercase with hyphens +- [ ] Has language suffix (`-py`, `-dotnet`, `-ts`, `-java`) unless cross-language +- [ ] Matches existing naming conventions in repository diff --git a/.agents/skills/skill-creator/references/azure-sdk-patterns.md b/.agents/skills/skill-creator/references/azure-sdk-patterns.md new file mode 100644 index 00000000000..057543dd57c --- /dev/null +++ b/.agents/skills/skill-creator/references/azure-sdk-patterns.md @@ -0,0 +1,539 @@ +# Azure SDK Patterns by Language + +Reference for creating skills that teach agents to write code following official Azure SDK guidelines. + +**Official Documentation:** https://azure.github.io/azure-sdk/ + +--- + +## Table of Contents + +1. [Core Principles (All Languages)](#core-principles-all-languages) +2. [Standard Naming Conventions](#standard-naming-conventions) +3. [Python Patterns](#python-patterns) +4. [.NET (C#) Patterns](#net-c-patterns) +5. [Java Patterns](#java-patterns) +6. [TypeScript/JavaScript Patterns](#typescriptjavascript-patterns) +7. [Authentication (All Languages)](#authentication-all-languages) +8. [Quick Reference Tables](#quick-reference-tables) + +--- + +## Core Principles (All Languages) + +Azure SDKs follow five design principles. Skills should reinforce these: + +| Principle | Meaning | +|-----------|---------| +| **Idiomatic** | Follow language conventions; feel natural to developers | +| **Consistent** | APIs feel like a single product from a single team | +| **Approachable** | Great docs, predictable defaults, progressive disclosure | +| **Diagnosable** | Clear logging, errors are actionable and human-readable | +| **Dependable** | No breaking changes without major version bump | + +**Consistency Priority:** Language conventions > Service conventions > Cross-language conventions + +--- + +## Standard Naming Conventions + +### Namespace/Package Format + +`..` + +| Group | Area | Examples | +|-------|------|----------| +| `ai` | AI/ML services | `Azure.AI.OpenAI`, `azure-ai-agents` | +| `data` | Databases | `Azure.Data.Cosmos`, `azure-cosmos` | +| `storage` | Storage services | `Azure.Storage.Blobs`, `@azure/storage-blob` | +| `identity` | Auth/Identity | `Azure.Identity`, `azure-identity` | +| `messaging` | Messaging | `Azure.Messaging.ServiceBus` | +| `security` | Security/Crypto | `Azure.Security.KeyVault` | + +### Standard Verb Prefixes (All Languages) + +| Verb | Behavior | Returns | +|------|----------|---------| +| `create` | Create new; fail if exists | Created item | +| `upsert` | Create or update (database-like) | Item | +| `set` | Create or update (dictionary-like) | Item | +| `update` | Fail if doesn't exist | Updated item | +| `get` | Retrieve single; error if missing | Item | +| `list` | Return collection (empty if none) | Pageable | +| `delete` | Succeed even if doesn't exist | void/None | +| `exists` | Check existence | boolean | +| `begin` | Start long-running operation | Poller | + +--- + +## Python Patterns + +### Client Naming + +```python +# Sync client +class ConfigurationClient: + pass + +# Async client - use Async prefix +class AsyncConfigurationClient: + pass +``` + +### Pagination: ItemPaged / AsyncItemPaged + +```python +from azure.core.paging import ItemPaged + +# Sync iteration +for item in client.list_items(): + print(item.name) + +# Page-by-page +for page in client.list_items().by_page(): + for item in page: + print(item.name) + +# With continuation token +for page in client.list_items().by_page(continuation_token="..."): + print(page) + +# Async iteration +async for item in async_client.list_items(): + print(item.name) +``` + +### Long-Running Operations: LROPoller / AsyncLROPoller + +```python +from azure.core.polling import LROPoller + +# Start LRO +poller: LROPoller[Result] = client.begin_create_resource(config) + +# Check status +if poller.done(): + result = poller.result() + +# Wait with timeout +result = poller.result(timeout=60) + +# Async LRO +async_poller = await async_client.begin_create_resource(config) +result = await async_poller.result() +``` + +### Context Managers + +```python +# Recommended pattern +with ConfigurationClient(endpoint, credential) as client: + setting = client.get_setting("key") + +# Async +async with AsyncConfigurationClient(endpoint, credential) as client: + setting = await client.get_setting("key") +``` + +### Error Handling + +```python +from azure.core.exceptions import ( + ResourceNotFoundError, + ResourceExistsError, + HttpResponseError, +) + +try: + item = client.get_item("key") +except ResourceNotFoundError: + print("Not found") +except HttpResponseError as e: + print(f"HTTP {e.status_code}: {e.message}") +``` + +### Docstring Format (Sphinx-style) + +```python +def get_setting(self, key: str, **kwargs) -> "ConfigurationSetting": + """Retrieve a configuration setting. + + :param key: The key of the setting. + :type key: str + :keyword timeout: Operation timeout in seconds. + :paramtype timeout: int + :returns: The configuration setting. + :rtype: ~azure.appconfig.ConfigurationSetting + :raises ~azure.core.exceptions.ResourceNotFoundError: If setting not found. + """ +``` + +--- + +## .NET (C#) Patterns + +### Client Naming + +```csharp +namespace Azure.Data.Configuration +{ + // Service client with Client suffix + public class ConfigurationClient { } + + // Options class + public class ConfigurationClientOptions : ClientOptions { } +} +``` + +### Response Wrapper: Response + +```csharp +// Single item +public Response GetSetting(string key); +public Task> GetSettingAsync(string key); + +// No content +public Response DeleteSetting(string key); +public Task DeleteSettingAsync(string key); +``` + +### Pagination: Pageable / AsyncPageable + +```csharp +// Sync +foreach (ConfigurationSetting setting in client.GetSettings()) +{ + Console.WriteLine(setting.Key); +} + +// Async +await foreach (ConfigurationSetting setting in client.GetSettingsAsync()) +{ + Console.WriteLine(setting.Key); +} +``` + +### Long-Running Operations: Operation + +```csharp +// With WaitUntil parameter +Operation operation = await client.StartAnalyzeAsync( + WaitUntil.Completed, // or WaitUntil.Started + document); + +AnalyzeResult result = operation.Value; + +// Manual polling +Operation operation = await client.StartAnalyzeAsync( + WaitUntil.Started, document); + +while (!operation.HasCompleted) +{ + await operation.UpdateStatusAsync(); + await Task.Delay(1000); +} +``` + +### Mocking Support + +```csharp +public class ConfigurationClient +{ + // Protected parameterless constructor for mocking + protected ConfigurationClient() { } + + // Virtual methods for mocking + public virtual Response GetSetting(string key); +} +``` + +### Error Handling + +```csharp +try +{ + var setting = await client.GetSettingAsync("key"); +} +catch (RequestFailedException ex) when (ex.Status == 404) +{ + Console.WriteLine("Not found"); +} +catch (RequestFailedException ex) +{ + Console.WriteLine($"Error: {ex.Status} - {ex.ErrorCode}"); +} +``` + +--- + +## Java Patterns + +### Client Naming + +```java +// Sync client +public final class ConfigurationClient { } + +// Async client +public final class ConfigurationAsyncClient { } + +// Builder (the ONLY way to create clients) +public final class ConfigurationClientBuilder { + public ConfigurationClient buildClient() { } + public ConfigurationAsyncClient buildAsyncClient() { } +} +``` + +### Builder Pattern + +```java +ConfigurationClient client = new ConfigurationClientBuilder() + .endpoint(endpoint) + .credential(new DefaultAzureCredentialBuilder().build()) + .serviceVersion(ConfigurationServiceVersion.V2023_10_01) + .buildClient(); +``` + +### Pagination: PagedIterable / PagedFlux + +```java +// Sync - standard for loop +for (ConfigurationSetting setting : client.listSettings()) { + System.out.println(setting.getKey()); +} + +// Sync - Stream API +client.listSettings().stream() + .filter(s -> s.getKey().startsWith("app")) + .forEach(System.out::println); + +// Async - Reactor +client.listSettings() + .subscribe(setting -> System.out.println(setting.getKey())); +``` + +### Long-Running Operations: SyncPoller / PollerFlux + +```java +// Sync +SyncPoller poller = + client.beginAnalyze(document); +poller.waitForCompletion(); +AnalyzeResult result = poller.getFinalResult(); + +// Async +client.beginAnalyze(document) + .last() + .flatMap(AsyncPollResponse::getFinalResult) + .subscribe(result -> System.out.println(result)); +``` + +### Reactor Types + +| Type | Purpose | +|------|---------| +| `Mono` | 0 or 1 item | +| `Flux` | 0 to N items | +| `PagedFlux` | Paginated collections | +| `PollerFlux` | Long-running operations | + +### Annotations + +```java +@ServiceClient(builder = ConfigurationClientBuilder.class) +public final class ConfigurationClient { + + @ServiceMethod(returns = ReturnType.SINGLE) + public ConfigurationSetting getSetting(String key) { } + + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedIterable listSettings() { } +} +``` + +--- + +## TypeScript/JavaScript Patterns + +### Package Naming + +```typescript +// Package: @azure/service-name (kebab-case) +// Client: ServiceClient (PascalCase with Client suffix) + +import { ServiceClient } from "@azure/service-name"; +``` + +### Pagination: PagedAsyncIterableIterator + +```typescript +// Iterate items +for await (const item of client.listItems()) { + console.log(item.name); +} + +// Iterate by page +for await (const page of client.listItems().byPage()) { + console.log(`Page has ${page.length} items`); +} + +// With continuation token +const iterator = client.listItems().byPage({ continuationToken }); +``` + +### Long-Running Operations + +```typescript +// Methods starting LRO use 'begin' prefix +const poller = await client.beginAnalyzeDocument(modelId, document, { + pollInterval: 2000 +}); + +// Wait for completion +const result = await poller.pollUntilDone(); + +// Serialize state for later +const state = poller.toString(); +const restored = await client.beginAnalyzeDocument(modelId, document, { + resumeFrom: state +}); +``` + +### Cancellation: AbortSignal + +```typescript +import { AbortController } from "@azure/abort-controller"; + +const controller = new AbortController(); +setTimeout(() => controller.abort(), 5000); + +try { + const item = await client.createItem({ + abortSignal: controller.signal + }); +} catch (e) { + if (e.name === "AbortError") { + console.log("Cancelled"); + } +} +``` + +### Options Pattern + +```typescript +interface CreateItemOptions { + abortSignal?: AbortSignalLike; + timeoutInMs?: number; // Duration suffix: InMs, InSeconds + onlyIfChanged?: boolean; // Conditional request +} +``` + +### Error Handling + +```typescript +import { RestError } from "@azure/core-rest-pipeline"; + +try { + await client.createItem(item); +} catch (e) { + // Check name, not instanceof + if (e.name === "RestError") { + console.error(`HTTP ${e.statusCode}: ${e.message}`); + } +} +``` + +--- + +## Authentication (All Languages) + +**Always use `DefaultAzureCredential` as the primary pattern:** + +### Python + +```python +from azure.identity import DefaultAzureCredential +credential = DefaultAzureCredential() +client = ServiceClient(endpoint, credential) +``` + +### .NET + +```csharp +var credential = new DefaultAzureCredential(); +var client = new ServiceClient(new Uri(endpoint), credential); +``` + +### Java + +```java +TokenCredential credential = new DefaultAzureCredentialBuilder().build(); +ServiceClient client = new ServiceClientBuilder() + .endpoint(endpoint) + .credential(credential) + .buildClient(); +``` + +### TypeScript + +```typescript +import { DefaultAzureCredential } from "@azure/identity"; +const credential = new DefaultAzureCredential(); +const client = new ServiceClient(endpoint, credential); +``` + +**Rules:** +- Never hardcode credentials +- Never persist/cache tokens manually (credential handles refresh) +- Use environment variables for configuration + +--- + +## Quick Reference Tables + +### Client Types by Language + +| Pattern | Python | .NET | Java | TypeScript | +|---------|--------|------|------|------------| +| Sync Client | `Client` | `Client` | `Client` | `Client` | +| Async Client | `AsyncClient` | N/A (Async methods) | `AsyncClient` | N/A (Promise) | +| Builder | N/A | N/A | `ClientBuilder` | N/A | + +### Pagination Types + +| Language | Sync | Async | +|----------|------|-------| +| Python | `ItemPaged[T]` | `AsyncItemPaged[T]` | +| .NET | `Pageable` | `AsyncPageable` | +| Java | `PagedIterable` | `PagedFlux` | +| TypeScript | N/A | `PagedAsyncIterableIterator` | + +### LRO Types + +| Language | Sync | Async | +|----------|------|-------| +| Python | `LROPoller[T]` | `AsyncLROPoller[T]` | +| .NET | `Operation` | `Operation` | +| Java | `SyncPoller` | `PollerFlux` | +| TypeScript | N/A | `PollerLike` | + +### Response Wrappers + +| Language | Single Item | Collection | +|----------|-------------|------------| +| Python | Direct return | `ItemPaged[T]` | +| .NET | `Response` | `Pageable` | +| Java | Direct return | `PagedIterable` | +| TypeScript | `Promise` | `PagedAsyncIterableIterator` | + +--- + +## Official Documentation Links + +- **General Guidelines:** https://azure.github.io/azure-sdk/general_introduction.html +- **Python:** https://azure.github.io/azure-sdk/python_design.html +- **.NET:** https://azure.github.io/azure-sdk/dotnet_introduction.html +- **Java:** https://azure.github.io/azure-sdk/java_introduction.html +- **TypeScript:** https://azure.github.io/azure-sdk/typescript_introduction.html + +When creating Azure SDK skills, reference these docs via the `microsoft-docs` MCP for current API signatures. diff --git a/.agents/skills/skill-creator/references/output-patterns.md b/.agents/skills/skill-creator/references/output-patterns.md new file mode 100644 index 00000000000..ddae31f1169 --- /dev/null +++ b/.agents/skills/skill-creator/references/output-patterns.md @@ -0,0 +1,187 @@ +# Output Patterns + +Patterns for producing consistent, high-quality output in skills. + +## Template Pattern + +Provide templates for output format. Match strictness to requirements. + +**For strict requirements (API responses, data formats):** + +```markdown +## Report structure + +ALWAYS use this exact template structure: + +# [Analysis Title] + +## Executive summary +[One-paragraph overview of key findings] + +## Key findings +- Finding 1 with supporting data +- Finding 2 with supporting data +- Finding 3 with supporting data + +## Recommendations +1. Specific actionable recommendation +2. Specific actionable recommendation +``` + +**For flexible guidance:** + +```markdown +## Report structure + +Sensible default format; adapt as needed: + +# [Analysis Title] + +## Executive summary +[Overview] + +## Key findings +[Adapt sections based on what you discover] + +## Recommendations +[Tailor to the specific context] +``` + +## Examples Pattern + +For output quality dependent on examples, provide input/output pairs: + +```markdown +## Commit message format + +Generate commit messages following these examples: + +**Example 1:** +Input: Added user authentication with JWT tokens +Output: +feat(auth): implement JWT-based authentication + +Add login endpoint and token validation middleware + +**Example 2:** +Input: Fixed bug where dates displayed incorrectly in reports +Output: +fix(reports): correct date formatting in timezone conversion + +Use UTC timestamps consistently across report generation + +Follow this style: type(scope): brief description, then detailed explanation. +``` + +Examples help agents understand desired style more clearly than descriptions alone. + +## Azure SDK Code Patterns + +### Client Initialization Template + +```python +# Standard Azure SDK client setup +import os +from azure.identity import DefaultAzureCredential +from azure. import Client + +credential = DefaultAzureCredential() +client = Client( + endpoint=os.environ["AZURE__ENDPOINT"], + credential=credential +) +``` + +### CRUD Method Template + +```python +# Create +item = client.create_( + name="example", + config=Config( + property1="value1", + property2="value2" + ) +) + +# Read +item = client.get_(item_id) + +# List (with pagination) +for item in client.list_(): + print(item.name) + +# Update +updated = client.update_(item_id, new_config) + +# Delete +client.delete_(item_id) +``` + +### Async Client Template + +```python +import asyncio +from azure.identity.aio import DefaultAzureCredential +from azure..aio import Client + +async def main(): + credential = DefaultAzureCredential() + async with Client(endpoint, credential) as client: + # Async operations + item = await client.get_(item_id) + + # Async pagination + async for item in client.list_(): + print(item.name) + +asyncio.run(main()) +``` + +### Error Handling Template + +```python +from azure.core.exceptions import ( + ResourceNotFoundError, + ResourceExistsError, + HttpResponseError, +) + +try: + result = client.get_(item_id) +except ResourceNotFoundError: + # Handle 404 + print(f"Resource {item_id} not found") +except ResourceExistsError: + # Handle 409 + print(f"Resource already exists") +except HttpResponseError as e: + # Handle other HTTP errors + print(f"HTTP {e.status_code}: {e.message}") +``` + +### Feature Comparison Table Template + +```markdown +## Clients + +| Client | Purpose | When to Use | +|--------|---------|-------------| +| `ServiceClient` | Core operations | Standard use cases | +| `AsyncServiceClient` | Async operations | High-throughput scenarios | +| `ServiceAdminClient` | Management | Creating/deleting resources | +``` + +### Environment Variables Template + +```markdown +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `AZURE__ENDPOINT` | Yes | Service endpoint URL | +| `AZURE__KEY` | No | API key (alternative to DefaultAzureCredential) | +| `AZURE_CLIENT_ID` | No | For service principal auth | +| `AZURE_TENANT_ID` | No | For service principal auth | +| `AZURE_CLIENT_SECRET` | No | For service principal auth | +``` diff --git a/.agents/skills/skill-creator/references/workflows.md b/.agents/skills/skill-creator/references/workflows.md new file mode 100644 index 00000000000..cd53c7a451e --- /dev/null +++ b/.agents/skills/skill-creator/references/workflows.md @@ -0,0 +1,145 @@ +# Workflow Patterns + +Patterns for structuring multi-step processes in skills. + +## Sequential Workflows + +For complex tasks, break operations into clear steps. Provide an overview at the start: + +```markdown +Filling a PDF form involves these steps: + +1. Analyze the form (run analyze_form.py) +2. Create field mapping (edit fields.json) +3. Validate mapping (run validate_fields.py) +4. Fill the form (run fill_form.py) +5. Verify output (run verify_output.py) +``` + +## Conditional Workflows + +For tasks with branching logic, guide through decision points: + +```markdown +1. Determine the modification type: + **Creating new content?** → Follow "Creation workflow" below + **Editing existing content?** → Follow "Editing workflow" below + +2. Creation workflow: [steps] +3. Editing workflow: [steps] +``` + +## Azure SDK Workflows + +### CRUD Lifecycle Pattern + +```markdown +## Working with [Resource] + +### Create +\`\`\`python +resource = client.create_resource(name="example", config={...}) +\`\`\` + +### Read +\`\`\`python +# Single item +resource = client.get_resource("resource-id") + +# List with pagination +for resource in client.list_resources(): + print(resource.name) +\`\`\` + +### Update +\`\`\`python +resource = client.update_resource("resource-id", new_config={...}) +\`\`\` + +### Delete +\`\`\`python +client.delete_resource("resource-id") +\`\`\` +``` + +### Long-Running Operation Pattern + +```markdown +## Processing [Resource] + +Long-running operations use the poller pattern: + +\`\`\`python +# Start operation +poller = client.begin_process_resource(resource_id, config) + +# Option 1: Wait for completion +result = poller.result() + +# Option 2: Poll with status updates +while not poller.done(): + print(f"Status: {poller.status()}") + time.sleep(5) +result = poller.result() + +# Option 3: Use callback +poller.add_done_callback(lambda r: print(f"Done: {r}")) +\`\`\` +``` + +### Agent Lifecycle Pattern (Azure AI Agents) + +```markdown +## Agent Workflow + +1. **Create Agent** with tools and instructions +2. **Create Thread** for conversation +3. **Add Messages** to thread +4. **Run Agent** on thread +5. **Process Response** (handle tool calls if needed) +6. **Cleanup** - delete agent when done + +\`\`\`python +# 1. Create +agent = client.create_agent(model="gpt-4o", instructions="...") + +# 2-4. Thread, Message, Run +thread = client.threads.create() +client.messages.create(thread_id=thread.id, content="...") +run = client.runs.create(thread_id=thread.id, agent_id=agent.id) + +# 5. Wait for completion +while run.status in ["queued", "in_progress"]: + run = client.runs.retrieve(thread_id=thread.id, run_id=run.id) + time.sleep(1) + +# 6. Cleanup +client.delete_agent(agent.id) +\`\`\` +``` + +### Error Recovery Pattern + +```markdown +## Error Handling + +\`\`\`python +from azure.core.exceptions import ( + ResourceNotFoundError, + ResourceExistsError, + HttpResponseError, +) + +try: + result = client.get_resource("id") +except ResourceNotFoundError: + # Handle missing resource + result = client.create_resource("id", default_config) +except HttpResponseError as e: + if e.status_code == 429: # Rate limited + time.sleep(e.retry_after or 60) + result = client.get_resource("id") + else: + raise +\`\`\` +``` diff --git a/.agents/skills/skill-creator/scripts/init_skill.py b/.agents/skills/skill-creator/scripts/init_skill.py new file mode 100644 index 00000000000..329ad4e5a71 --- /dev/null +++ b/.agents/skills/skill-creator/scripts/init_skill.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +""" +Skill Initializer - Creates a new skill from template + +Usage: + init_skill.py --path + +Examples: + init_skill.py my-new-skill --path skills/public + init_skill.py my-api-helper --path skills/private + init_skill.py custom-skill --path /custom/location +""" + +import sys +from pathlib import Path + + +SKILL_TEMPLATE = """--- +name: {skill_name} +description: [TODO: Complete and informative explanation of what the skill does and when to use it. Include WHEN to use this skill - specific scenarios, file types, or tasks that trigger it.] +--- + +# {skill_title} + +## Overview + +[TODO: 1-2 sentences explaining what this skill enables] + +## Structuring This Skill + +[TODO: Choose the structure that best fits this skill's purpose. Common patterns: + +**1. Workflow-Based** (best for sequential processes) +- Works well when there are clear step-by-step procedures +- Example: DOCX skill with "Workflow Decision Tree" → "Reading" → "Creating" → "Editing" +- Structure: ## Overview → ## Workflow Decision Tree → ## Step 1 → ## Step 2... + +**2. Task-Based** (best for tool collections) +- Works well when the skill offers different operations/capabilities +- Example: PDF skill with "Quick Start" → "Merge PDFs" → "Split PDFs" → "Extract Text" +- Structure: ## Overview → ## Quick Start → ## Task Category 1 → ## Task Category 2... + +**3. Reference/Guidelines** (best for standards or specifications) +- Works well for brand guidelines, coding standards, or requirements +- Example: Brand styling with "Brand Guidelines" → "Colors" → "Typography" → "Features" +- Structure: ## Overview → ## Guidelines → ## Specifications → ## Usage... + +**4. Capabilities-Based** (best for integrated systems) +- Works well when the skill provides multiple interrelated features +- Example: Product Management with "Core Capabilities" → numbered capability list +- Structure: ## Overview → ## Core Capabilities → ### 1. Feature → ### 2. Feature... + +Patterns can be mixed and matched as needed. Most skills combine patterns (e.g., start with task-based, add workflow for complex operations). + +Delete this entire "Structuring This Skill" section when done - it's just guidance.] + +## [TODO: Replace with the first main section based on chosen structure] + +[TODO: Add content here. See examples in existing skills: +- Code samples for technical skills +- Decision trees for complex workflows +- Concrete examples with realistic user requests +- References to scripts/templates/references as needed] + +## Resources + +This skill includes example resource directories that demonstrate how to organize different types of bundled resources: + +### scripts/ +Executable code (Python/Bash/etc.) that can be run directly to perform specific operations. + +**Examples from other skills:** +- PDF skill: `fill_fillable_fields.py`, `extract_form_field_info.py` - utilities for PDF manipulation +- DOCX skill: `document.py`, `utilities.py` - Python modules for document processing + +**Appropriate for:** Python scripts, shell scripts, or any executable code that performs automation, data processing, or specific operations. + +**Note:** Scripts may be executed without loading into context, but can still be read by Claude for patching or environment adjustments. + +### references/ +Documentation and reference material intended to be loaded into context to inform Claude's process and thinking. + +**Examples from other skills:** +- Product management: `communication.md`, `context_building.md` - detailed workflow guides +- BigQuery: API reference documentation and query examples +- Finance: Schema documentation, company policies + +**Appropriate for:** In-depth documentation, API references, database schemas, comprehensive guides, or any detailed information that Claude should reference while working. + +### assets/ +Files not intended to be loaded into context, but rather used within the output Claude produces. + +**Examples from other skills:** +- Brand styling: PowerPoint template files (.pptx), logo files +- Frontend builder: HTML/React boilerplate project directories +- Typography: Font files (.ttf, .woff2) + +**Appropriate for:** Templates, boilerplate code, document templates, images, icons, fonts, or any files meant to be copied or used in the final output. + +--- + +**Any unneeded directories can be deleted.** Not every skill requires all three types of resources. +""" + +EXAMPLE_SCRIPT = '''#!/usr/bin/env python3 +""" +Example helper script for {skill_name} + +This is a placeholder script that can be executed directly. +Replace with actual implementation or delete if not needed. + +Example real scripts from other skills: +- pdf/scripts/fill_fillable_fields.py - Fills PDF form fields +- pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images +""" + +def main(): + print("This is an example script for {skill_name}") + # TODO: Add actual script logic here + # This could be data processing, file conversion, API calls, etc. + +if __name__ == "__main__": + main() +''' + +EXAMPLE_REFERENCE = """# Reference Documentation for {skill_title} + +This is a placeholder for detailed reference documentation. +Replace with actual reference content or delete if not needed. + +Example real reference docs from other skills: +- product-management/references/communication.md - Comprehensive guide for status updates +- product-management/references/context_building.md - Deep-dive on gathering context +- bigquery/references/ - API references and query examples + +## When Reference Docs Are Useful + +Reference docs are ideal for: +- Comprehensive API documentation +- Detailed workflow guides +- Complex multi-step processes +- Information too lengthy for main SKILL.md +- Content that's only needed for specific use cases + +## Structure Suggestions + +### API Reference Example +- Overview +- Authentication +- Endpoints with examples +- Error codes +- Rate limits + +### Workflow Guide Example +- Prerequisites +- Step-by-step instructions +- Common patterns +- Troubleshooting +- Best practices +""" + +EXAMPLE_ASSET = """# Example Asset File + +This placeholder represents where asset files would be stored. +Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed. + +Asset files are NOT intended to be loaded into context, but rather used within +the output Claude produces. + +Example asset files from other skills: +- Brand guidelines: logo.png, slides_template.pptx +- Frontend builder: hello-world/ directory with HTML/React boilerplate +- Typography: custom-font.ttf, font-family.woff2 +- Data: sample_data.csv, test_dataset.json + +## Common Asset Types + +- Templates: .pptx, .docx, boilerplate directories +- Images: .png, .jpg, .svg, .gif +- Fonts: .ttf, .otf, .woff, .woff2 +- Boilerplate code: Project directories, starter files +- Icons: .ico, .svg +- Data files: .csv, .json, .xml, .yaml + +Note: This is a text placeholder. Actual assets can be any file type. +""" + + +def title_case_skill_name(skill_name): + """Convert hyphenated skill name to Title Case for display.""" + return ' '.join(word.capitalize() for word in skill_name.split('-')) + + +def init_skill(skill_name, path): + """ + Initialize a new skill directory with template SKILL.md. + + Args: + skill_name: Name of the skill + path: Path where the skill directory should be created + + Returns: + Path to created skill directory, or None if error + """ + # Determine skill directory path + skill_dir = Path(path).resolve() / skill_name + + # Check if directory already exists + if skill_dir.exists(): + print(f"❌ Error: Skill directory already exists: {skill_dir}") + return None + + # Create skill directory + try: + skill_dir.mkdir(parents=True, exist_ok=False) + print(f"✅ Created skill directory: {skill_dir}") + except Exception as e: + print(f"❌ Error creating directory: {e}") + return None + + # Create SKILL.md from template + skill_title = title_case_skill_name(skill_name) + skill_content = SKILL_TEMPLATE.format( + skill_name=skill_name, + skill_title=skill_title + ) + + skill_md_path = skill_dir / 'SKILL.md' + try: + skill_md_path.write_text(skill_content) + print("✅ Created SKILL.md") + except Exception as e: + print(f"❌ Error creating SKILL.md: {e}") + return None + + # Create resource directories with example files + try: + # Create scripts/ directory with example script + scripts_dir = skill_dir / 'scripts' + scripts_dir.mkdir(exist_ok=True) + example_script = scripts_dir / 'example.py' + example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name)) + example_script.chmod(0o755) + print("✅ Created scripts/example.py") + + # Create references/ directory with example reference doc + references_dir = skill_dir / 'references' + references_dir.mkdir(exist_ok=True) + example_reference = references_dir / 'api_reference.md' + example_reference.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title)) + print("✅ Created references/api_reference.md") + + # Create assets/ directory with example asset placeholder + assets_dir = skill_dir / 'assets' + assets_dir.mkdir(exist_ok=True) + example_asset = assets_dir / 'example_asset.txt' + example_asset.write_text(EXAMPLE_ASSET) + print("✅ Created assets/example_asset.txt") + except Exception as e: + print(f"❌ Error creating resource directories: {e}") + return None + + # Print next steps + print(f"\n✅ Skill '{skill_name}' initialized successfully at {skill_dir}") + print("\nNext steps:") + print("1. Edit SKILL.md to complete the TODO items and update the description") + print("2. Customize or delete the example files in scripts/, references/, and assets/") + print("3. Run the validator when ready to check the skill structure") + + return skill_dir + + +def main(): + if len(sys.argv) < 4 or sys.argv[2] != '--path': + print("Usage: init_skill.py --path ") + print("\nSkill name requirements:") + print(" - Hyphen-case identifier (e.g., 'data-analyzer')") + print(" - Lowercase letters, digits, and hyphens only") + print(" - Max 40 characters") + print(" - Must match directory name exactly") + print("\nExamples:") + print(" init_skill.py my-new-skill --path skills/public") + print(" init_skill.py my-api-helper --path skills/private") + print(" init_skill.py custom-skill --path /custom/location") + sys.exit(1) + + skill_name = sys.argv[1] + path = sys.argv[3] + + print(f"🚀 Initializing skill: {skill_name}") + print(f" Location: {path}") + print() + + result = init_skill(skill_name, path) + + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.agents/skills/skill-creator/scripts/package_skill.py b/.agents/skills/skill-creator/scripts/package_skill.py new file mode 100644 index 00000000000..5cd36cb16e1 --- /dev/null +++ b/.agents/skills/skill-creator/scripts/package_skill.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +""" +Skill Packager - Creates a distributable .skill file of a skill folder + +Usage: + python utils/package_skill.py [output-directory] + +Example: + python utils/package_skill.py skills/public/my-skill + python utils/package_skill.py skills/public/my-skill ./dist +""" + +import sys +import zipfile +from pathlib import Path +from quick_validate import validate_skill + + +def package_skill(skill_path, output_dir=None): + """ + Package a skill folder into a .skill file. + + Args: + skill_path: Path to the skill folder + output_dir: Optional output directory for the .skill file (defaults to current directory) + + Returns: + Path to the created .skill file, or None if error + """ + skill_path = Path(skill_path).resolve() + + # Validate skill folder exists + if not skill_path.exists(): + print(f"❌ Error: Skill folder not found: {skill_path}") + return None + + if not skill_path.is_dir(): + print(f"❌ Error: Path is not a directory: {skill_path}") + return None + + # Validate SKILL.md exists + skill_md = skill_path / "SKILL.md" + if not skill_md.exists(): + print(f"❌ Error: SKILL.md not found in {skill_path}") + return None + + # Run validation before packaging + print("🔍 Validating skill...") + valid, message = validate_skill(skill_path) + if not valid: + print(f"❌ Validation failed: {message}") + print(" Please fix the validation errors before packaging.") + return None + print(f"✅ {message}\n") + + # Determine output location + skill_name = skill_path.name + if output_dir: + output_path = Path(output_dir).resolve() + output_path.mkdir(parents=True, exist_ok=True) + else: + output_path = Path.cwd() + + skill_filename = output_path / f"{skill_name}.skill" + + # Create the .skill file (zip format) + try: + with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: + # Walk through the skill directory + for file_path in skill_path.rglob('*'): + if file_path.is_file(): + # Calculate the relative path within the zip + arcname = file_path.relative_to(skill_path.parent) + zipf.write(file_path, arcname) + print(f" Added: {arcname}") + + print(f"\n✅ Successfully packaged skill to: {skill_filename}") + return skill_filename + + except Exception as e: + print(f"❌ Error creating .skill file: {e}") + return None + + +def main(): + if len(sys.argv) < 2: + print("Usage: python utils/package_skill.py [output-directory]") + print("\nExample:") + print(" python utils/package_skill.py skills/public/my-skill") + print(" python utils/package_skill.py skills/public/my-skill ./dist") + sys.exit(1) + + skill_path = sys.argv[1] + output_dir = sys.argv[2] if len(sys.argv) > 2 else None + + print(f"📦 Packaging skill: {skill_path}") + if output_dir: + print(f" Output directory: {output_dir}") + print() + + result = package_skill(skill_path, output_dir) + + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.agents/skills/skill-creator/scripts/quick_validate.py b/.agents/skills/skill-creator/scripts/quick_validate.py new file mode 100644 index 00000000000..d9fbeb75ee1 --- /dev/null +++ b/.agents/skills/skill-creator/scripts/quick_validate.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +""" +Quick validation script for skills - minimal version +""" + +import sys +import os +import re +import yaml +from pathlib import Path + +def validate_skill(skill_path): + """Basic validation of a skill""" + skill_path = Path(skill_path) + + # Check SKILL.md exists + skill_md = skill_path / 'SKILL.md' + if not skill_md.exists(): + return False, "SKILL.md not found" + + # Read and validate frontmatter + content = skill_md.read_text() + if not content.startswith('---'): + return False, "No YAML frontmatter found" + + # Extract frontmatter + match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if not match: + return False, "Invalid frontmatter format" + + frontmatter_text = match.group(1) + + # Parse YAML frontmatter + try: + frontmatter = yaml.safe_load(frontmatter_text) + if not isinstance(frontmatter, dict): + return False, "Frontmatter must be a YAML dictionary" + except yaml.YAMLError as e: + return False, f"Invalid YAML in frontmatter: {e}" + + # Define allowed properties + ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata'} + + # Check for unexpected properties (excluding nested keys under metadata) + unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES + if unexpected_keys: + return False, ( + f"Unexpected key(s) in SKILL.md frontmatter: {', '.join(sorted(unexpected_keys))}. " + f"Allowed properties are: {', '.join(sorted(ALLOWED_PROPERTIES))}" + ) + + # Check required fields + if 'name' not in frontmatter: + return False, "Missing 'name' in frontmatter" + if 'description' not in frontmatter: + return False, "Missing 'description' in frontmatter" + + # Extract name for validation + name = frontmatter.get('name', '') + if not isinstance(name, str): + return False, f"Name must be a string, got {type(name).__name__}" + name = name.strip() + if name: + # Check naming convention (hyphen-case: lowercase with hyphens) + if not re.match(r'^[a-z0-9-]+$', name): + return False, f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)" + if name.startswith('-') or name.endswith('-') or '--' in name: + return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens" + # Check name length (max 64 characters per spec) + if len(name) > 64: + return False, f"Name is too long ({len(name)} characters). Maximum is 64 characters." + + # Extract and validate description + description = frontmatter.get('description', '') + if not isinstance(description, str): + return False, f"Description must be a string, got {type(description).__name__}" + description = description.strip() + if description: + # Check for angle brackets + if '<' in description or '>' in description: + return False, "Description cannot contain angle brackets (< or >)" + # Check description length (max 1024 characters per spec) + if len(description) > 1024: + return False, f"Description is too long ({len(description)} characters). Maximum is 1024 characters." + + return True, "Skill is valid!" + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python quick_validate.py ") + sys.exit(1) + + valid, message = validate_skill(sys.argv[1]) + print(message) + sys.exit(0 if valid else 1) \ No newline at end of file diff --git a/.dev/features.json b/.dev/features.json new file mode 100644 index 00000000000..a79cb6c30ea --- /dev/null +++ b/.dev/features.json @@ -0,0 +1,228 @@ +{ + "$schema": "./features.schema.json", + "$comment": "Single source of truth for all M365 Agents Toolkit features. Edit this file to add/update features. The integration test framework (packages/fx-core/tests/integration/) loads this automatically. See .github/instructions/features.instructions.md for full documentation.", + "features": [ + { + "id": "default-bot", + "name": "Echo Bot", + "description": "A simple echo bot built with Bot Framework SDK. Responds to messages in Teams channels and 1:1 chats. Good starting point for conversational agents.", + "category": "Bot", + "templateName": "default-bot", + "languages": ["typescript", "javascript", "python", "csharp"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "bot-type", + "capabilities": ["conversational", "teams-channel", "personal-chat"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "cli", "vscode"], + "adoSuiteId": 24569101, + "adoTestCaseCount": 15 + }, + { + "id": "basic-tab", + "name": "Basic Tab", + "description": "A simple Teams tab application — a web page embedded in Teams as a personal or channel tab. Uses React for the frontend. No SSO authentication included.", + "category": "Tab", + "templateName": "non-sso-tab", + "languages": ["typescript", "csharp"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "tab-type", + "capabilities": ["web-app", "embedded-iframe", "personal-tab", "channel-tab"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "cli", "vscode"], + "adoSuiteId": 24569106, + "adoTestCaseCount": 5 + }, + { + "id": "basic-custom-engine-agent", + "name": "Basic Custom Engine Agent", + "description": "A custom engine agent that uses your own AI model (e.g. Azure OpenAI) to power a conversational bot in Teams. Includes Teams AI Library integration.", + "category": "Custom Engine Agent", + "templateName": "basic-custom-engine-agent", + "languages": ["typescript", "javascript", "python"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "custom-engine-agent-type", + "capabilities": ["ai-powered", "custom-model", "conversational", "teams-ai-library"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "cli", "vscode"], + "adoSuiteId": 34834051, + "adoTestCaseCount": 21 + }, + { + "id": "weather-agent", + "name": "Weather Agent", + "description": "A custom engine agent that demonstrates function calling / tool use. Includes a weather lookup tool that the AI model can invoke during conversation.", + "category": "Custom Engine Agent", + "templateName": "weather-agent", + "languages": ["typescript", "javascript"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "custom-engine-agent-type", + "capabilities": ["ai-powered", "function-calling", "tool-use", "custom-model"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core"], + "adoSuiteId": 34648283, + "adoTestCaseCount": 12 + }, + { + "id": "teams-collaborator-agent", + "name": "Teams Collaborator Agent", + "description": "A multi-agent system for Teams using Azure OpenAI. Demonstrates agent-to-agent collaboration patterns with Teams AI Library.", + "category": "Custom Engine Agent", + "templateName": "teams-collaborator-agent", + "languages": ["typescript"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "custom-engine-agent-type", + "capabilities": ["ai-powered", "multi-agent", "azure-openai", "collaboration"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core"], + "adoSuiteId": 35527236, + "adoTestCaseCount": 3 + }, + { + "id": "custom-copilot-basic", + "name": "AI Chat Bot", + "description": "An AI-powered chat bot for Teams using Azure OpenAI or OpenAI. Includes Teams AI Library with prompt management and conversation state.", + "category": "AI Agent", + "templateName": "custom-copilot-basic", + "languages": ["typescript", "javascript", "python"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "teams-agent-and-app-type", + "capabilities": ["ai-powered", "openai-integration", "prompt-management", "conversational"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "cli"], + "adoSuiteId": 27042287, + "adoTestCaseCount": 22 + }, + { + "id": "custom-copilot-rag-azure-ai-search", + "name": "AI Agent with AI Search (RAG Azure AI Search)", + "description": "An AI agent for Teams that retrieves data from Azure AI Search to augment its responses. Uses RAG pattern with vector/hybrid search.", + "category": "AI Agent", + "templateName": "custom-copilot-rag-azure-ai-search", + "languages": ["typescript", "javascript", "python"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "teams-agent-and-app-type", + "capabilities": ["ai-powered", "rag", "azure-ai-search", "vector-search"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core"], + "adoSuiteId": 27689412 + }, + { + "id": "custom-copilot-rag-customize", + "name": "AI Agent with Custom Data (RAG Customize)", + "description": "An AI agent for Teams with customizable data sources. Uses RAG pattern with user-defined data retrieval logic.", + "category": "AI Agent", + "templateName": "custom-copilot-rag-customize", + "languages": ["typescript", "javascript", "python"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "teams-agent-and-app-type", + "capabilities": ["ai-powered", "rag", "customizable-data", "data-retrieval"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core"], + "adoSuiteId": 27689419 + }, + { + "id": "declarative-agent-basic", + "name": "Declarative Agent (Basic)", + "description": "A basic declarative agent for Microsoft 365 Copilot, defined via a JSON manifest without custom code. Extends Copilot with custom instructions and knowledge.", + "category": "Declarative Agent", + "templateName": "copilot-gpt-basic", + "languages": ["common", "csharp"], + "lifecycles": ["scaffold", "provision", "publish"], + "projectType": "copilot-agent-type", + "capabilities": ["copilot-extension", "declarative", "no-code", "manifest-driven"], + "generatedFiles": ["m365agents.yml", "appPackage/declarativeAgent.json"], + "entryPoints": ["fx-core", "cli", "vscode"], + "adoSuiteId": 27971458, + "adoTestCaseCount": 41 + }, + { + "id": "declarative-agent-action-none", + "name": "Declarative Agent with API Plugin (No Auth)", + "description": "A declarative agent with an API plugin action built from scratch. No authentication required for the API endpoint.", + "category": "Declarative Agent", + "templateName": "api-plugin-from-scratch", + "languages": ["typescript", "javascript"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "copilot-agent-type", + "capabilities": ["copilot-extension", "api-plugin", "no-auth", "openapi"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "cli", "vscode"], + "adoSuiteId": 27971458 + }, + { + "id": "declarative-agent-action-oauth", + "name": "Declarative Agent with API Plugin (OAuth)", + "description": "A declarative agent with an API plugin action that uses OAuth 2.0 for authentication against the API endpoint.", + "category": "Declarative Agent", + "templateName": "api-plugin-from-scratch-oauth", + "languages": ["typescript", "javascript"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "copilot-agent-type", + "capabilities": ["copilot-extension", "api-plugin", "oauth", "openapi"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "vscode"], + "adoSuiteId": 27971458 + }, + { + "id": "declarative-agent-action-bearer", + "name": "Declarative Agent with API Plugin (Bearer)", + "description": "A declarative agent with an API plugin action that uses Bearer token authentication against the API endpoint.", + "category": "Declarative Agent", + "templateName": "api-plugin-from-scratch-bearer", + "languages": ["typescript", "javascript"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "copilot-agent-type", + "capabilities": ["copilot-extension", "api-plugin", "bearer-token", "openapi"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "vscode"], + "adoSuiteId": 27971458 + }, + { + "id": "graph-connector", + "name": "Graph Connector", + "description": "A Microsoft Graph connector that ingests external data into Microsoft 365. Makes external content searchable and available in Copilot and Microsoft Search.", + "category": "Connector", + "templateName": "graph-connector", + "languages": ["typescript"], + "lifecycles": ["scaffold", "provision", "deploy"], + "projectType": "graph-connector-type", + "capabilities": ["microsoft-graph", "external-data", "search-integration", "copilot-grounding"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core"], + "adoSuiteId": 32019603, + "adoTestCaseCount": 2 + }, + { + "id": "message-extension-v2", + "name": "Message Extension", + "description": "A Teams message extension (v2) that lets users search and share content directly within the Teams compose box. Supports search-based commands.", + "category": "Messaging Extension", + "templateName": "default-message-extension", + "languages": ["typescript", "python"], + "lifecycles": ["scaffold", "provision", "deploy", "publish"], + "projectType": "me-type", + "capabilities": ["search-command", "compose-extension", "adaptive-cards"], + "generatedFiles": ["m365agents.yml"], + "entryPoints": ["fx-core", "cli"], + "adoSuiteId": 34869329, + "adoTestCaseCount": 6 + } + ], + "trackedOnly": [ + { + "id": "custom-copilot-rag-custom-api", + "name": "RAG Custom API", + "templateName": "custom-copilot-rag-custom-api", + "reason": "Requires API spec input — uses CustomEngineAgentWithExistingApiSpecGenerator", + "adoSuiteId": 27588348 + }, + { + "id": "foundry-agent-to-m365", + "name": "Foundry Proxy Agent", + "templateName": "foundry-proxy-agent", + "reason": "C#/.NET only (VS templates) — no TS/JS/Python VSC template exists locally", + "adoSuiteId": 36750068, + "adoTestCaseCount": 4 + } + ] +} diff --git a/.dev/features.schema.json b/.dev/features.schema.json new file mode 100644 index 00000000000..8eaeba6f380 --- /dev/null +++ b/.dev/features.schema.json @@ -0,0 +1,132 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "M365 Agents Toolkit Feature Registry", + "description": "Single source of truth for all M365 Agents Toolkit features. Used by integration tests and AI agents.", + "type": "object", + "required": ["features"], + "additionalProperties": false, + "properties": { + "$schema": { "type": "string" }, + "$comment": { "type": "string" }, + "features": { + "type": "array", + "description": "Features with full test coverage metadata.", + "items": { "$ref": "#/definitions/feature" } + }, + "trackedOnly": { + "type": "array", + "description": "Features tracked for ADO traceability but not yet testable in the integration suite.", + "items": { "$ref": "#/definitions/trackedOnlyFeature" } + } + }, + "definitions": { + "feature": { + "type": "object", + "required": ["id", "name", "description", "category", "templateName", "languages", "lifecycles", "projectType", "capabilities", "generatedFiles", "entryPoints"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "description": "Unique kebab-case identifier for the feature." + }, + "name": { + "type": "string", + "description": "Human-readable display name." + }, + "description": { + "type": "string", + "description": "One-sentence description of the feature." + }, + "category": { + "type": "string", + "enum": ["Bot", "Tab", "Custom Engine Agent", "AI Agent", "Declarative Agent", "Connector", "Messaging Extension"], + "description": "Feature category." + }, + "templateName": { + "type": "string", + "description": "Must match a value from the TemplateNames enum in fx-core." + }, + "languages": { + "type": "array", + "items": { + "type": "string", + "enum": ["typescript", "javascript", "python", "csharp", "common"] + }, + "minItems": 1, + "description": "Supported programming languages." + }, + "lifecycles": { + "type": "array", + "items": { + "type": "string", + "enum": ["scaffold", "provision", "deploy", "publish"] + }, + "minItems": 1, + "description": "Lifecycle stages covered by integration tests." + }, + "projectType": { + "type": "string", + "description": "The project type identifier used by the toolkit." + }, + "capabilities": { + "type": "array", + "items": { "type": "string" }, + "description": "Feature capabilities and characteristics." + }, + "generatedFiles": { + "type": "array", + "items": { "type": "string" }, + "description": "Key files generated when scaffolding this feature." + }, + "entryPoints": { + "type": "array", + "items": { + "type": "string", + "enum": ["fx-core", "cli", "vscode"] + }, + "minItems": 1, + "description": "Entry points that support this feature." + }, + "adoSuiteId": { + "type": "integer", + "description": "Azure DevOps test suite ID for traceability." + }, + "adoTestCaseCount": { + "type": "integer", + "description": "Number of manual test cases in the ADO suite." + } + } + }, + "trackedOnlyFeature": { + "type": "object", + "required": ["id", "name", "templateName", "reason"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "description": "Unique kebab-case identifier." + }, + "name": { + "type": "string", + "description": "Human-readable display name." + }, + "templateName": { + "type": "string", + "description": "Template name for reference." + }, + "reason": { + "type": "string", + "description": "Why this feature is tracked-only and not fully testable." + }, + "adoSuiteId": { + "type": "integer", + "description": "Azure DevOps test suite ID." + }, + "adoTestCaseCount": { + "type": "integer", + "description": "Number of manual test cases in the ADO suite." + } + } + } + } +} diff --git a/.dev/plans/2026-03-24-feature-tracked-integration-tests.md b/.dev/plans/2026-03-24-feature-tracked-integration-tests.md new file mode 100644 index 00000000000..659ab12b7bf --- /dev/null +++ b/.dev/plans/2026-03-24-feature-tracked-integration-tests.md @@ -0,0 +1,33 @@ +# Plan: Feature-Tracked Integration Test Framework + +**Status:** In Progress +**Created:** 2026-03-24 +**Updated:** 2026-03-24 + +## Goal + +Build a registry-driven integration test framework with a single source of truth (`featureRegistry.ts`) that serves both as AI-readable feature documentation and test driver across all four entry points (fx-core, CLI, Server, VS Code extension). Produces JSON + HTML coverage matrix reports. + +## Tasks + +- [ ] Create featureRegistry.ts with types, enums, and curated features +- [ ] Create coverageTracker.ts for runtime pass/fail tracking +- [ ] Create reportGenerator.ts for JSON + HTML output +- [ ] Create rootHooks.ts (Mocha afterAll wiring) +- [ ] Create testBuilders.ts (scaffoldTest, provisionTest factories) +- [ ] Rewrite createProject.test.ts to registry-driven loop +- [ ] Rewrite provision.test.ts to builder pattern +- [ ] Create .mocharc.integration.js + update package.json +- [ ] Create features.instructions.md + update fx-core.instructions.md +- [ ] Build and verify all tests pass + +## Notes + +- Only 6 of 12 originally planned templates have local directories available +- Templates with local dirs: default-bot, basic-tab, basic-custom-engine-agent, custom-copilot-basic, graph-connector, weather-agent +- message-extension-v2, teams-collaborator-agent, foundry-agent-to-m365 also available +- CLI/Server/VSCode integration tests deferred to follow-up (Phase 4-6 of design) + +## Log + +- 2026-03-24 — Plan created, starting Phase 1 implementation diff --git a/.dev/plans/2026-03-24-integration-test-refactor.md b/.dev/plans/2026-03-24-integration-test-refactor.md new file mode 100644 index 00000000000..3a930c1211a --- /dev/null +++ b/.dev/plans/2026-03-24-integration-test-refactor.md @@ -0,0 +1,45 @@ +# Plan: Refactor fx-core for Testability + Add Integration Tests + +**Status:** Complete +**Created:** 2026-03-24 +**Updated:** 2026-03-24 + +## Goal + +Refactor fx-core to make key singletons and globals injectable, then add integration tests that exercise the full lifecycle (scaffold/provision/deploy) with mocked HTTP clients. + +## Tasks + +- [x] Make teamsDevPortalClient injectable (getter/setter pattern) +- [x] Evaluate global TOOLS access — decided setTools() is sufficient seam +- [x] Add resetGlobalVars utility for test cleanup +- [x] Make globalState path configurable via TEAMSFX_STATE_DIR env var +- [x] Create integration test infrastructure (helpers.ts with mock Tools, providers, etc.) +- [x] Write fx-core scaffold integration test (5 test cases) +- [x] Write fx-core provision integration test (1 test case with mocked teamsDevPortalClient) +- [x] Add test:integration / test:unit scripts to package.json +- [x] Build and verify all existing + new tests pass + +## Results + +- TypeScript compilation: clean (0 errors) +- Integration tests: 6/6 passing (500ms) +- Unit tests: 3503 passing, 14 failing (all pre-existing — kiota binary + test tool checker) + +## Files Modified + +- `src/client/teamsDevPortalClient.ts` — Added getTeamsDevPortalClient()/setTeamsDevPortalClient() +- `src/common/globalVars.ts` — Added resetGlobalVars() +- `src/common/globalState.ts` — Added TEAMSFX_STATE_DIR env var support +- `src/index.ts` — Exported new functions + +## Files Created + +- `tests/integration/helpers.ts` — Mock infrastructure +- `tests/integration/createProject.test.ts` — 5 scaffold test cases +- `tests/integration/provision.test.ts` — 1 provision test case + +## Log + +- 2026-03-24 — Plan created +- 2026-03-24 — All tasks complete. 6 integration tests passing, no regressions in unit tests. diff --git a/.dev/plans/2026-03-27-full-stack-refactor.md b/.dev/plans/2026-03-27-full-stack-refactor.md new file mode 100644 index 00000000000..7e48184292b --- /dev/null +++ b/.dev/plans/2026-03-27-full-stack-refactor.md @@ -0,0 +1,48 @@ +# Plan: Full-Stack Refactoring — Phase 1 Foundation + +**Status:** Complete +**Created:** 2026-03-27 +**Updated:** 2026-03-30 + +## Goal + +Implement Phase 1 of the refactoring plan: create `packages/core-next/` with the new `@microsoft/teamsfx-core` v4.0.0 foundation — merged API contracts, AtkContext, Operation pipeline, TemplateDescriptor, DriverDescriptor, and AtkError model. + +## Tasks + +- [x] Scaffold `packages/core-next/` with package.json, tsconfig.json +- [x] Create `src/api/` — merge contracts from `@microsoft/teamsfx-api` + - [x] Error types (FxError, UserError, SystemError) + - [x] Types (Inputs, OptionItem, etc.) + - [x] Context interface + - [x] Question model (BaseQuestion, UI configs) + - [x] User interaction (UserInteraction) + - [x] Utilities (LogProvider, TelemetryReporter, TokenProvider, CryptoProvider) + - [x] Constants (Platform, Stage, etc.) + - [x] CLI types (CLICommand, CLIContext) + - [x] Generator interface (IGenerator) +- [x] Define AtkContext interface (replaces TOOLS singleton) +- [x] Define Operation interface + runOperation wrapper +- [x] Define TemplateDescriptor + TemplateRegistry +- [x] Define DriverDescriptor + DriverRegistry +- [x] Define AtkError model +- [x] Create package index.ts with all exports +- [x] Register `packages/core-next` in pnpm-workspace.yaml +- [x] Add `TEAMSFX_V4_CORE` feature flag in fx-core +- [x] Build and verify compilation +- [x] Set up core-next test infrastructure (.mocharc.js, .nycrc, test scripts) +- [x] Write core-next unit tests (59 tests across 11 files) +- [x] Write core-next integration tests (11 tests across 2 files) + +## Notes + +- Existing npm name: `@microsoft/teamsfx-core`, bumped to v4.0.0 +- New folder: `packages/core-next/` (coexists with `packages/fx-core/`) +- `@microsoft/teamsfx-api` contracts absorbed into `src/api/` +- Feature flag gates any runtime switch from old to new + +## Log + +- 2026-03-27 — Plan created, starting implementation +- 2026-03-27 — Phase 1 complete: all tasks done, build passes with zero errors +- 2026-03-30 — Test infrastructure added: 59 unit tests + 11 integration tests, all passing diff --git a/.dev/plans/2026-03-27-phase2-cli-rewrite.md b/.dev/plans/2026-03-27-phase2-cli-rewrite.md new file mode 100644 index 00000000000..4d940a2620d --- /dev/null +++ b/.dev/plans/2026-03-27-phase2-cli-rewrite.md @@ -0,0 +1,50 @@ +# Plan: Phase 2 — CLI Rewrite (`packages/cli-next/`) + +**Status:** Complete +**Created:** 2026-03-27 +**Updated:** 2026-03-30 + +## Goal + +Rebuild the CLI from scratch using Commander.js, consuming `@microsoft/teamsfx-core` v4 operations directly. Published under `@microsoft/m365agentstoolkit-cli` v4.0.0 with binary names `atk`, `teamsapp`, `m365agentstoolkit-cli`. + +## Tasks + +- [x] Scaffold `packages/cli-next/` with package.json, tsconfig.json +- [x] Create entry points: `cli.js`, `cliold.js` (bin stubs) +- [x] Create `src/program.ts` — Commander.js program setup +- [x] Create `src/ui/` — interactive prompt adapter (CLIUserInteraction) +- [x] Create `src/output/` — formatting system (colorize, table, JSON) +- [x] Create `src/telemetry/` — telemetry reporter +- [x] Create `src/error.ts` — CLI error types +- [x] Create project lifecycle commands: new, provision, deploy, publish, preview +- [x] Create account commands: auth login azure/m365, auth logout, auth show +- [x] Create environment commands: env add, env list, env reset +- [x] Create teamsapp commands: validate, package, publish, update, doctor +- [x] Create add commands: add action, add capability, add auth-config, add spfx-web-part +- [x] Create utility commands: list, list samples, list templates, upgrade, set, validate, share +- [x] Create m365 commands: m365-sideload, m365-unacquire, m365-launch-info +- [x] Create permission commands: permission grant, permission status +- [x] Create entra-app commands: entra-app update +- [x] Create regenerate commands: regenerate, regenerate action +- [x] Create `src/index.ts` entry point +- [x] Register `packages/cli-next` in pnpm-workspace.yaml +- [x] Build and verify compilation +- [x] Set up cli-next test infrastructure (.mocharc.js, .nycrc, test scripts, devDependencies) +- [x] Write cli-next unit tests (19 tests across 4 files) +- [x] Write cli-next integration tests (4 tests across 1 file) + +## Notes + +- Commander.js replaces custom recursive-descent parser +- @inquirer/prompts kept for interactive mode +- Same 3 binary entry points as current CLI +- All commands are thin wrappers calling core-next operations +- Non-interactive via `--non-interactive` flag or `CI_ENABLED=true` +- JSON output via `--output json` + +## Log + +- 2026-03-27 — Plan created, starting implementation +- 2026-03-27 — Phase 2 complete: all commands registered, build passes with zero errors (92 output files) +- 2026-03-30 — Test infrastructure added: 19 unit tests + 4 integration tests, all passing diff --git a/.dev/plans/2026-04-02-lifecycle-e2e.md b/.dev/plans/2026-04-02-lifecycle-e2e.md new file mode 100644 index 00000000000..782a1f512a8 --- /dev/null +++ b/.dev/plans/2026-04-02-lifecycle-e2e.md @@ -0,0 +1,85 @@ +# Plan: Provision/Deploy Lifecycle E2E under cli-next + +**Status:** Phases 1–4b Complete, Phase 5 Integration Tests Complete (Manual E2E pending) +**Created:** 2026-04-02 +**Updated:** 2026-04-02 + +## Goal + +Enable complete provision, deploy, and publish lifecycle execution via cli-next by wiring real auth providers, registering builtin drivers, and adding interactive prompts. + +## Tasks + +### Phase 1: Driver Bootstrap (P0) +- [x] Call `registerBuiltinDrivers()` at CLI boot in `packages/cli-next/src/index.ts` + +### Phase 2: Auth Providers (P0) +- [x] Create `packages/cli-next/src/auth/constants.ts` — shared auth constants +- [x] Create `packages/cli-next/src/auth/utils.ts` — token parsing, online check, JWT utilities +- [x] Create `packages/cli-next/src/auth/cacheAccess.ts` — AES-256-GCM encrypted token cache with keytar +- [x] Create `packages/cli-next/src/auth/codeFlowLogin.ts` — MSAL interactive + silent token engine +- [x] Create `packages/cli-next/src/auth/m365Login.ts` — M365TokenProvider implementation +- [x] Create `packages/cli-next/src/auth/azureLogin.ts` — AzureAccountProvider with subscription listing +- [x] Create `packages/cli-next/src/auth/azureLoginCI.ts` — Service principal auth (CI/headless) +- [x] Create `packages/cli-next/src/auth/index.ts` — `createTokenProvider()` factory +- [x] Replace stub auth in `packages/cli-next/src/context.ts` with real `createTokenProvider()` +- [x] Wire auth commands in `packages/cli-next/src/commands/account.ts` +- [x] Add dependencies: `@azure/msal-node`, `@azure/identity`, `@azure/arm-subscriptions`, `async-mutex`, `open`, `keytar` + +### Phase 3: Interactive UI (P1) +- [x] Wire `@inquirer/prompts` in `packages/cli-next/src/ui/userInteraction.ts` (selectOption, selectOptions, inputText, confirm) + +### Phase 4: Missing Drivers (P1) +- [x] Audit all `templates/**/m365agents.yml` for `uses:` IDs vs registered drivers +- [x] Implement `teamsApp/update` driver in core-next (`packages/core-next/src/drivers/builtin/teamsApp/update.ts`) +- [x] Implement `teamsApp/extendToM365` driver in core-next (`packages/core-next/src/drivers/builtin/teamsApp/extendToM365.ts`) +- [x] Create `M365PackageService` sideloading client (`packages/core-next/src/clients/m365/packageService.ts`) +- [x] Register both new drivers in `builtin/index.ts` (18→20 drivers) +- [x] Update `M365_DRIVERS` set in `analyze.ts` (12→14 entries) +- [x] Add `form-data` dependency to core-next +- [x] Update all tests with hardcoded driver counts (18→21) +- [~] Ignore phantom `azureStorage/deploy`, `azureStorage/config` (per user request) + +### Phase 4b: Echo Bot Deploy Driver (P1) +- [x] Implement `cli/runNpmCommand` driver (`packages/core-next/src/drivers/builtin/cli/runNpmCommand.ts`) +- [x] Register in `builtin/index.ts` (20→21 drivers) +- [x] Update test driver counts (20→21) + +### Phase 5: E2E Verification (P2) +- [x] Data-driven integration test framework (features.json → template YAML → lifecycle execution) + - `tests/integration/helpers/featureRegistry.ts` — features.json typed loader with query helpers + - `tests/integration/helpers/templateYamlLoader.ts` — template YAML reader with Mustache stripping + - `tests/integration/helpers/driverStubs.ts` — sinon stubs for service-call drivers with mock outputs + - `tests/integration/helpers/mockContext.ts` — fully-stubbed AtkContext for integration tests + - `tests/integration/driverCoverage.tests.ts` — guard-rail: verifies all template drivers registered + - `tests/integration/pipeline/provisionPipeline.tests.ts` — 14 provision tests from features.json + - `tests/integration/pipeline/deployPipeline.tests.ts` — 13 deploy tests from features.json + - `tests/integration/pipeline/publishPipeline.tests.ts` — 13 publish tests from features.json +- [ ] Manual E2E: DA Basic provision (M365 auth) +- [ ] Manual E2E: Bot Basic provision (Azure auth + ARM) +- [ ] Manual E2E: Bot Basic deploy (zip deploy) +- [ ] Manual E2E: DA Basic publish + +## Notes + +- **CryptoProvider** is NOT needed — `ctx.crypto` is optional and unused by lifecycle code +- **lifecycle.ts (actions)** needed no changes — already correctly delegates to `runOperation()` +- **Token cache reuses `~/.fx/account/`** for seamless v3→v4 migration +- **Deploy pipeline is simpler** than provision — no subscription/RG selection (values already in env) +- Auth code ported from proven v3 CLI (~2,500 LOC), adapted to core-next interfaces +- `keytar` is optional — tokens stored unencrypted if keytar is unavailable + +## Decisions + +- Port v3 auth code rather than rewrite from scratch +- Reuse `~/.fx/account/` cache directory for migration compatibility +- `keytar` as optionalDependency — graceful degradation on systems without OS keychain +- Defer Windows broker (NativeBrokerPlugin) to post-MVP — dynamic require with try/catch + +## Log + +- 2026-04-02 — Plan created; Phases 1-3 implemented; 486 core-next + 47 cli-next tests passing +- 2026-04-02 — Phase 4 complete; 2 new drivers (teamsApp/update, teamsApp/extendToM365) + M365PackageService; 486 core-next + 47 cli-next tests passing +- 2026-04-02 — Phase 4b complete; cli/runNpmCommand driver for echo bot deploy; 21 total drivers; 486 + 47 tests passing +- 2026-04-02 — Phase 5 integration tests complete; data-driven pipeline tests from features.json; 55 integration tests (3 coverage + 14 provision + 13 deploy + 13 publish + 12 existing); 486 core-next + 47 unit + 55 integration = 588 total tests passing +- 2026-04-03 — CI-next: created `.github/workflows/ci-next.yml` (build → lint, format-check, unit-test, integration-test); ESLint flat configs + Prettier configs for core-next & cli-next; excluded v4 packages from old `unit-test.yml`; added `setup:next` script + `"next"` range in setup-project action; 0 lint errors, format clean, 588 tests passing diff --git a/.dev/plans/2026-04-07-add-commands.md b/.dev/plans/2026-04-07-add-commands.md new file mode 100644 index 00000000000..3cd97552308 --- /dev/null +++ b/.dev/plans/2026-04-07-add-commands.md @@ -0,0 +1,31 @@ +# Plan: Wire 5 DA Quick-Win CLI Commands + +**Status:** Complete +**Created:** 2026-04-07 +**Updated:** 2026-04-07 + +## Goal + +Wire 5 stub CLI commands to their existing core-next DA operations, moving coverage from 14/39 (36%) to 19/39 (49%). Flags-only (no interactive prompts). Drop `add spfx-web-part`. + +## Tasks + +- [x] Phase 1: Add `getAgentManifestPath(projectPath)` to core-next `declarativeAgent/manifest/` +- [x] Phase 2: Create `extendToM365Op` operation wrapping the existing driver +- [x] Phase 3: Create 5 CLI action handlers (addAction, addCapability, addAuthConfig, setSensitivityLabel, m365Sideload) +- [x] Phase 4: Rewrite command definitions (add.ts, misc.ts, m365.ts) to use `wrapHandlerWithContext` +- [x] Phase 5: Core-next unit tests (getAgentManifestPath, extendToM365Op) +- [x] Phase 6: CLI-next unit tests (5 action handlers) +- [x] Phase 7: Build, lint, and full test verification + +## Notes + +- `add spfx-web-part` dropped (no core-next backing) +- `addMCPActionOp` NOT wired — too many required fields for flags-only +- `--agent-manifest-path` auto-discovered from project when not provided +- All flags-only, CI-friendly (no interactive prompts) + +## Log + +- 2026-04-07 — Plan created +- 2026-04-07 — All 7 phases complete. 17 new tests (6 core-next + 11 cli-next), all passing. Totals: 492 core-next unit, 78 cli-next unit. 5 commands wired, `add spfx-web-part` removed. diff --git a/.github/actions/setup-project/action.yml b/.github/actions/setup-project/action.yml index 63bb524fa70..342e9ad5431 100644 --- a/.github/actions/setup-project/action.yml +++ b/.github/actions/setup-project/action.yml @@ -37,6 +37,9 @@ runs: "e2e") npm run setup:e2e ;; + "next") + npm run setup:next + ;; "all"|*) npm run setup ;; diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index c9d39e8444d..00000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,91 +0,0 @@ -# Coding Style Guidelines - -When generating or editing code in this repository: - -- **Line Endings**: Use LF (Unix-style) line endings for all source code files. Exception: Use CRLF for localization files under `**/package.nls.*.json` only. -- **Indentation**: Use 2 spaces for TypeScript/JavaScript files -- **Quotes**: Use double quotes for strings in TypeScript/JavaScript - ---- - -# Architecture Overview -The toolkit follows a **layered architecture** with clear separation of concerns: - -┌─────────────────────────────────────────────────────────────────────┐ -│ VS Code Extension Layer │ -│ (packages/vscode-extension) │ -│ - UI Commands, Handlers, TreeView, CodeLens Providers │ -└──────────────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────────────▼──────────────────────────────────────┐ -│ FX-Core Layer │ -│ (packages/fx-core) │ -│ - Project Generation, Lifecycle, Drivers, Manifest Utilities │ -└──────────────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────────────▼──────────────────────────────────────┐ -│ API Layer │ -│ (packages/api) │ -│ - Type Definitions, Interfaces, Question Models │ -└──────────────────────────────┬──────────────────────────────────────┘ - │ -┌──────────────────────────────▼──────────────────────────────────────┐ -│ Manifest Layer │ -│ (packages/manifest) │ -│ - App Manifest Types, Converters, OOP Wrappers │ -│ - Three manifest types: Teams, Declarative Agent, API Plugin │ -└─────────────────────────────────────────────────────────────────────┘ - ---- - -# Package-Specific Instructions - -Detailed instructions for individual packages are available in `.github/instructions/`: - -| Package | Instructions File | Key Exports | -|---------|-------------------|-------------| -| `@microsoft/app-manifest` (packages/manifest) | `.github/instructions/manifest.instructions.md` | `TeamsManifest`, `DeclarativeAgentManifest`, `APIPluginManifest`, `AppManifestUtils`, Wrappers | - -> **Note**: When working with a specific package, read the corresponding instructions file for detailed documentation about the package's architecture, APIs, and patterns - ---- - -# Manifest Package Quick Reference - -The manifest package (`packages/manifest`) provides TypeScript types for Microsoft 365 app manifests: - -| Manifest Type | Purpose | Wrapper | Latest Type | -|---------------|---------|---------|-------------| -| **Teams Manifest** | Core M365 apps (bots, tabs, extensions) | `TeamsManifestWrapper` | `TeamsManifestLatest` | -| **Declarative Agent** | AI agents with instructions & actions | `DeclarativeAgentManifestWrapper` | `DeclarativeAgentManifestLatest` | -| **API Plugin** | REST API plugin capabilities | `PluginManifestWrapper` | `APIPluginManifestLatest` | - -**Key utilities:** -- `AppManifestUtils` - Read/write/validate manifests -- `*Converter` classes - JSON ↔ typed object conversion -- Wrappers provide fluent APIs for manipulation - -**Deprecated types** (use generated types instead): -- `TeamsAppManifest` → use `TeamsManifest` -- `DeclarativeCopilotManifestSchema` → use `DeclarativeAgentManifest` -- `PluginManifestSchema` → use `APIPluginManifest` -- `ManifestUtil` → use `AppManifestUtils` - ---- - -# Unit Testing Guidelines - -When fixing unit tests for a package: - -1. **Navigate to the package directory** before running tests -2. **Run the full test suite** to identify failures: - ```bash - npm run test:unit - ``` -3. **For large test suites or targeted debugging**, run specific test files directly: - ```bash - npx nyc mocha --no-timeouts --require ts-node/register - ``` -4. **Fix errors iteratively** - run tests after each fix to verify - -> **Tip**: When there are many failing tests, start with the specific file causing issues to reduce feedback loop time. diff --git a/.github/instructions/api.instructions.md b/.github/instructions/api.instructions.md new file mode 100644 index 00000000000..683970d0ae9 --- /dev/null +++ b/.github/instructions/api.instructions.md @@ -0,0 +1,101 @@ +--- +description: "Use when editing or creating interfaces, error types, question models, or contracts in the api package. Covers backward compatibility, interface evolution, re-exports, and Result pattern." +applyTo: "packages/api/**/*.ts" +--- + +# API Package Conventions + +## Role + +This is the **public contract surface** for the current (v3) toolkit. All other v3 packages depend on it. Changes here have the widest blast radius. + +> **v4 Migration Note:** All contracts from this package have been merged into +> `packages/core-next/src/api/` (`@microsoft/teamsfx-core` v4.0.0). New v4 packages +> (`cli-next`, future `vscode-extension`) import from `@microsoft/teamsfx-core` instead. +> This package remains the source of truth for v3 packages (`fx-core`, `cli`, `server`, +> `vscode-extension`). Keep both in sync until v3 is fully deprecated. + +## Backward Compatibility + +- Mark removed or replaced members with `@deprecated` and a migration note before removing +- Add new functionality via **optional properties** (`?:`) — never change existing required properties +- Prefer **discriminated unions** over type assertions when adding new variants: + +```typescript +// Good — extend with new variant +export type CLICommandOption = CLIBooleanOption | CLIStringOption | CLIArrayOption; + +export interface CLIStringOption extends CLICommandOptionBase { + type: "string"; + choices?: string[]; // optional extension +} + +// Bad — change existing required field type +``` + +- When removing a re-exported symbol, keep the re-export with `@deprecated` for at least one minor version + +## Re-Exports + +`index.ts` re-exports key dependencies so consumers get a single import surface: + +```typescript +export * from "neverthrow"; // Result, ok(), err() +export * from "@microsoft/app-manifest"; // Manifest types +``` + +- Consumers import `ok`, `err`, `Result`, manifest types from `@microsoft/teamsfx-api` — not from the underlying packages +- Adding a new re-export is a public API change — be deliberate + +## Error Types + +`FxError` is the base interface. Two concrete classes: + +| Class | When | User sees | +|-------|------|-----------| +| `UserError` | Bad input, missing config, auth issues | Help link, action buttons | +| `SystemError` | Service failure, internal bugs | Issue link | + +Constructor via options object: + +```typescript +new UserError({ + source: string, // component name (required) + name?: string, // stable error name for telemetry + message?: string, // English log message + displayMessage?: string, // localized user-facing message + helpLink?: string, // aka.ms link + error?: Error, // wrapped inner error +}); +``` + +- `name` appears in telemetry — keep it stable across releases +- `displayMessage` is what users see — always localized +- `message` is for logs — always English + +## Question Model (QM) + +Defines the interactive question tree used by VS Code, CLI, and Visual Studio: + +- `BaseQuestion` → `UserInputQuestion` with `type` discriminator +- Question types: `singleSelect`, `multiSelect`, `text`, `singleFile`, `multiFile`, `folder`, `confirm` +- Dynamic options via `LocalFunc` — computed from previous answers at runtime +- Validation via `StringValidation` (pattern, enum, minLength) or custom `ValidateFunc` + +Adding a new question type: +1. Add the type string to the union in `question.ts` +2. Define the interface extending `UserInputQuestion` +3. Implement rendering in CLI (`userInteraction.ts`) and VS Code (`qm/`) + +## Interface Design Guidelines + +- Prefer `unknown` over `any` for untyped data; narrow with type guards +- Use `readonly` for properties that should not be mutated after creation +- Document every public interface member with JSDoc — these are consumed by external contributors +- Group related interfaces in dedicated files; export via barrel in `qm/index.ts` or `utils/index.ts` + +## Testing + +- Tests verify interface contracts implicitly via type checking +- Error serialization/deserialization is explicitly tested +- When adding a new interface, add tests that construct and validate the shape diff --git a/.github/instructions/cli.instructions.md b/.github/instructions/cli.instructions.md new file mode 100644 index 00000000000..eaf72cc330e --- /dev/null +++ b/.github/instructions/cli.instructions.md @@ -0,0 +1,379 @@ +--- +description: "Use when editing or creating CLI commands, prompts, argument parsing, login flows, telemetry, or error handling in the CLI package (v3 or v4)." +applyTo: "packages/cli/**/*.ts, packages/cli-next/**/*.ts" +--- + +# CLI Package Conventions + +## Architecture + +``` +cli.js (entry) + → activate() in index.ts + → CLIEngine.start(rootCommand) + → findCommand() → parseArgs() → validateOptions() → handler() +``` + +## Command Model + +Commands are declarative `CLICommand` objects in `src/commands/models/`: + +```typescript +export const myCommand: CLICommand = { + name: "my-command", + description: "What this command does", + options: [...MyCommandOptions], // from fx-core or local definitions + handler: async (ctx: CLIContext): Promise> => { + // delegate to fx-core + const result = await ctx.core.myOperation(ctx.inputs); + return result; + }, + telemetry: { event: TelemetryEvent.MyCommand }, +}; +``` + +### Adding a New Command + +1. Create `src/commands/models/mycommand.ts` with a `CLICommand` object +2. Export from `src/commands/models/index.ts` +3. Add to `commands` array in `src/commands/models/root.ts` +4. Add localized strings in `src/resource/` + +## Command Engine (7 Phases) + +1. **Find command** — tree traversal with alias support +2. **Parse args** — custom parser: `--key=value`, `--key value`, `-k value` +3. **Version check** — `--version` flag +4. **Help display** — `--help` flag +5. **Validate options** — skipped in interactive mode (reserved options only) +6. **Version compatibility** — project version check + phantom migration +7. **Execute handler** — wrapped in `Correlator.run()` for tracing + +## Interactive vs Non-Interactive + +- **Interactive** (default): CLI options are discarded; question model drives prompts via `CLIUserInteraction` +- **Non-interactive** (`CI_ENABLED=true`): All options must be passed as CLI args; prompts auto-return defaults +- `reservedOptionNamesInInteractiveMode` — options always parsed even in interactive mode + +## User Interaction + +`CLIUserInteraction` wraps `@inquirer/prompts`: + +| Method | Prompt | +|--------|--------| +| `singleSelect()` | Custom list prompt (`customizedListPrompt.ts`) | +| `multiSelect()` | Custom checkbox prompt (`customizedCheckboxPrompt.ts`) | +| `input()` | `@inquirer/prompts.input` with green theme | +| `password()` | `@inquirer/prompts.password` with mask | +| `confirm()` | `@inquirer/prompts.confirm` | + +- `ScreenManager` manages terminal output (pause/continue around prompts) +- Respects `CI_ENABLED=true` — returns defaults without prompting + +## Error Classes + +All extend `UserError` from `@microsoft/teamsfx-api`: + +- `MissingRequiredOptionError` — required CLI option not provided +- `MissingRequiredArgumentError` — positional arg missing +- `InvalidChoiceError` — value not in allowed choices +- `UnknownCommandError` — with edit-distance "Did you mean?" suggestions +- `UnknownOptionError`, `ArgumentConflictError` + +Messages are localized via `src/resource/` strings. + +## Telemetry + +```typescript +CliTelemetry.sendTelemetryEvent(eventName, properties, measurements); +CliTelemetry.sendTelemetryErrorEvent(eventName, error, properties, measurements); +``` + +- Auto-injected: `component`, `commandName`, `projectId`, `correlationId`, `runFrom` (CI platform detection) +- `withRootFolder()` sets shared project-scoped properties +- Secrets in `commandFull` are auto-masked + +## Argument Parsing + +Custom recursive descent parser — no external library (yargs, commander): + +- Boolean: `--flag` (true), `--flag false` +- String: `--key value` or `--key=value` +- Array: `--arr a,b,c` (comma-separated) +- Short names: `-k value` + +## Login Flows + +Singleton providers in `src/commonlib/`: +- `AzureLogin` — MSAL with interactive code flow +- `M365Login` — MSAL with `CryptoCachePlugin` for secure token caching +- Windows: native broker plugin for WAM integration +- Flow: check cache → interactive browser consent → cache token + +## Output & Color + +- `CLILogger` implements `LogProvider` with chalk color support +- `ScreenManager.writeLine()` for formatted output +- `colorize.ts` wraps chalk: red=error, green=success, cyan=links +- `Progress` class manages progress bars with `start()`/`end(success)` lifecycle + +--- + +# CLI v4 (`packages/cli-next`) — Next-Generation Architecture + +> **Status:** Phase 4b complete + CI. Lifecycle commands (provision/deploy/publish) wired with real auth +> providers and 21 registered drivers. 38/39 templates fully supported (only typeSpec/compile missing). +> Registry-driven command factory, action layer, interactive UI, MSAL auth. +> CI: `ci-next.yml` (lint, format-check, unit-test, integration-test for both core-next & cli-next). +> E2E: `e2e-test-next.yml` (daily schedule + PR + manual; failure summary via `$GITHUB_STEP_SUMMARY`). +> **Feature flag:** `TEAMSFX_V4_CORE` (default: off) +> +> **Scripts:** `lint`, `format`, `format:check`, `test:unit`, `test:integration`, `build` +> **ESLint config:** flat config (`eslint.config.mjs`) with `shared` + `header` (no `promise` — too many false positives in CLI stubs). + +## Architecture + +``` +cli.js (entry — sets TEAMSFX_CLI_BIN_NAME) + → index.ts + → buildProgram() via Commander.js + → command groups (project, account, env, teamsapp, add, list, m365, permission, entraApp, regenerate, misc) + → wrapHandler(commandName, handler) for telemetry + error handling + → wrapHandlerWithContext(name, handler) for handlers needing AtkContext +``` + +### Context Layer (`src/context.ts`) + +`createCliContext(projectPath?)` — creates an `AtkContext` wiring: +- `logger` → `CLILogProvider` +- `telemetry` → adapter wrapping `CliTelemetryReporter` +- `ui` → `CLIUserInteraction` +- `auth` → real MSAL-based `TokenProvider` via `createTokenProvider()` from `src/auth/` +- `correlationId` → `crypto.randomUUID()` + +### Auth Module (`src/auth/`) + +Real MSAL-based authentication ported from v3 CLI (`packages/cli/src/commonlib/`): + +| File | Purpose | +|------|---------| +| `constants.ts` | Shared auth constants (client IDs, authority URLs, scopes) | +| `utils.ts` | Token parsing, online check, JWT claim extraction | +| `cacheAccess.ts` | AES-256-GCM encrypted MSAL token cache with optional keytar | +| `codeFlowLogin.ts` | MSAL interactive (auth code + browser) + silent token engine | +| `m365Login.ts` | `M365TokenProvider` implementation (M365 + Graph + App Studio scopes) | +| `azureLogin.ts` | `AzureAccountProvider` with interactive login + subscription listing | +| `azureLoginCI.ts` | Service principal auth for CI/headless environments | +| `index.ts` | `createTokenProvider()` factory — detects CI mode, creates appropriate providers | + +- Token cache shared at `~/.fx/account/` for v3→v4 migration compatibility +- `keytar` is an optional dependency — graceful fallback to unencrypted cache +- Dependencies: `@azure/msal-node`, `@azure/identity`, `@azure/arm-subscriptions`, `async-mutex`, `open` + +### Command Factory (`src/commands/factory.ts`) + +Registry-driven subcommand generation — the extensibility core: + +- `buildNewCommands(parent, registry, opts?)` — reads `TemplateRegistry`, groups by category, + creates Commander subcommands: `atk new da basic`, `atk new bot echo`, etc. +- `mapQuestionToOption(spec)` — converts `QuestionSpec` → Commander `Option` using + `cliName`, `cliShortName`, `cliDescription`, `isBoolean` from the Question interface +- Category slug mapping (configurable): `declarative-agent→da`, `custom-engine-agent→cea`, + `ai-agent→ai`, `message-extension→me`, `bot→bot`, `tab→tab`, `connector→connector`, `office-addin→addin` +- Adding a new template to the registry automatically creates a new CLI subcommand + +### Actions Layer (`src/actions/`) + +Pure async functions that bridge CLI options to core-next operations (testable without Commander): + +| Action | File | Core-next operation | +|--------|------|--------------------| +| `createProjectAction` | `actions/createProject.ts` | `runOperation(createProjectOp)` | +| `provisionAction` | `actions/lifecycle.ts` | `runOperation(provisionOp)` | +| `deployAction` | `actions/lifecycle.ts` | `runOperation(deployOp)` | +| `publishAction` | `actions/lifecycle.ts` | `runOperation(publishOp)` | +| `envListAction` | `actions/environment.ts` | `environment.listEnvironments()` | +| `envAddAction` | `actions/environment.ts` | `environment.addEnvironment()` | +| `envResetAction` | `actions/environment.ts` | `environment.resetEnvironment()` | +| `validateAction` | `actions/teamsapp.ts` | `runOperation(validateManifestOp \| validateAppPackageOp)` | +| `packageAction` | `actions/teamsapp.ts` | `runOperation(packageAppOp)` | +| `listTemplatesAction` | `actions/listTemplates.ts` | `registry.list()` → table rows | +| `addActionAction` | `actions/addAction.ts` | `runOperation(addExistingPluginOp)` | +| `addCapabilityAction` | `actions/addCapability.ts` | `runOperation(addKnowledgeOp)` | +| `addAuthConfigAction` | `actions/addAuthConfig.ts` | `runOperation(injectOAuthActionOp \| injectApiKeyActionOp)` | +| `setSensitivityLabelAction` | `actions/setSensitivityLabel.ts` | `runOperation(setSensitivityLabelOp)` | +| `m365SideloadAction` | `actions/m365Sideload.ts` | `runOperation(extendToM365Op)` | + +## Key Differences from v3 + +| Aspect | v3 (`packages/cli`) | v4 (`packages/cli-next`) | +|--------|---------------------|-------------------------| +| Arg parser | Custom recursive-descent | Commander.js | +| Command model | Declarative `CLICommand` objects | Commander.js `.command()` / `.option()` / `.action()` | +| Dependencies | `@microsoft/teamsfx-api` + `@microsoft/teamsfx-core` | `@microsoft/teamsfx-core` v4 (merged API) | +| Error types | Same names, imported from `@microsoft/teamsfx-api` | Same names, imported from `@microsoft/teamsfx-core` | +| Prompt library | `@inquirer/prompts` (custom wrappers) | `@inquirer/prompts` (wired: select, checkbox, input, confirm) | +| Result pattern | `neverthrow` via `@microsoft/teamsfx-api` | `neverthrow` via `@microsoft/teamsfx-core` | + +## Package Layout + +``` +packages/cli-next/ + src/ + index.ts — Entry point, program parse + context.ts — createCliContext() factory (AtkContext for CLI, real auth providers) + handler.ts — wrapHandler(), wrapHandlerWithContext(), renderPostActions() + error.ts — CLI error classes (UserError/SystemError) + logger.ts — CLILogProvider (LogProvider impl) + auth/ — Real MSAL-based auth providers + constants.ts — Client IDs, authority URLs, scopes + utils.ts — Token parsing, JWT utilities + cacheAccess.ts — AES-256-GCM encrypted token cache (optional keytar) + codeFlowLogin.ts — MSAL interactive + silent token engine + m365Login.ts — M365TokenProvider implementation + azureLogin.ts — AzureAccountProvider (interactive + subscription listing) + azureLoginCI.ts — Service principal auth (CI/headless) + index.ts — createTokenProvider() factory + actions/ — Pure action functions (testable without Commander) + createProject.ts — createProjectAction(ctx, input) + lifecycle.ts — provisionAction, deployAction, publishAction + environment.ts — envListAction, envAddAction, envResetAction + teamsapp.ts — validateAction, packageAction + listTemplates.ts — listTemplatesAction(registry) + addAction.ts — addActionAction (wires to addExistingPluginOp) + addCapability.ts — addCapabilityAction (wires to addKnowledgeOp) + addAuthConfig.ts — addAuthConfigAction (wires to injectOAuth/ApiKeyOps) + setSensitivityLabel.ts — setSensitivityLabelAction (wires to setSensitivityLabelOp) + m365Sideload.ts — m365SideloadAction (wires to extendToM365Op) + commands/ + index.ts — buildProgram() assembles all Commander.js commands + factory.ts — buildNewCommands() registry-driven subcommand generator + project.ts — new (factory-driven), provision, deploy, publish, preview, upgrade + account.ts — auth show/login/logout + env.ts — env add/list/reset (wired to actions) + teamsapp.ts — validate/package (wired to actions), publish/update/doctor + add.ts — add action/capability/auth-config (wired to DA ops) + list.ts — list templates (wired to action) /samples + m365.ts — m365-sideload (wired to extendToM365Op) /unacquire/launch-info + permission.ts — permission grant/status + entraApp.ts — entra-app update + regenerate.ts — regenerate/regenerate action + misc.ts — validate, set (sensitivityLabel wired), share, init + output/ + colorize.ts — chalk-based 9-TextType color system + formatter.ts — JSON + table (cli-table3) output + telemetry/ + index.ts — CliTelemetryReporter singleton (lazy App Insights init) + appInsightsTransport.ts — Thin wrapper around applicationinsights TelemetryClient + sanitize.ts — PII redaction (file paths, tokens, emails) + ui/ + userInteraction.ts — CLIUserInteraction (interactive + non-interactive) +``` + +## Telemetry (v4) + +Lazy-initialised Application Insights transport — **no-op until `aiKey` is present** in `package.json`. + +``` +start() reads package.json → cliTelemetry.init(aiKey, version) + → AppInsightsTransport.init(key, commonProperties) + → appInsights.setup().setAutoCollect*(false).start() + → every command: wrapHandler() → sendEvent("cmd-start") / sendEvent("cmd-end") / sendErrorEvent("cmd-error") + → sanitizeProperties() strips tokens, passwords, emails, user file paths + → flush() in handler finally block +``` + +**Key files:** + +| File | Purpose | +|------|---------| +| `telemetry/index.ts` | `CliTelemetryReporter` — lazy init, shared properties, debug mode via `TEAMSFX_TELEMETRY_TEST` | +| `telemetry/appInsightsTransport.ts` | `AppInsightsTransport` — App Insights client with all auto-collection disabled, disk retry caching | +| `telemetry/sanitize.ts` | `anonymizeFilePaths()`, `sanitizeProperties()` — PII redaction before sending | +| `context.ts` | `telemetryAdapter` bridges `CliTelemetryReporter` → core's `TelemetryReporter` interface | +| `handler.ts` | `wrapHandler()` — auto-instruments every command with start/end/error events + duration | + +**Activation gate:** +- Dev/local: `aiKey` is a dev instrumentation key in `package.json` +- Production: CD workflow (`cd.yml`) replaces `aiKey` with `${{ secrets.CLI_PUBLIC_AIKEY }}` +- No key (empty string): all methods stay no-ops — identical to previous stub behaviour + +**Common properties** (set on the App Insights client): `common.os`, `common.platformversion`, `common.cliversion`, `common.machineid` + +**Dependencies:** `applicationinsights@^1.8.10`, `node-machine-id@^1.1.12` + +## Binary Names + +- `atk` — primary entry point +- `teamsapp` — deprecated (shows deprecation warning) +- `m365agentstoolkit-cli` — long-form alias + +## Adding a Command (v4) + +1. Add a new function in the appropriate `src/commands/.ts` file +2. Use `program.command("name").description("...").option("--flag", "...").action(wrapHandler("name", handler))` +3. Register in `buildProgram()` in `src/commands/index.ts` +4. The handler receives Commander.js parsed options — no custom parser logic needed + +## Testing (v4) + +Test infrastructure mirrors fx-core/cli pattern (Mocha + Chai + Sinon + NYC): + +| Config | Purpose | +|--------|---------| +| `.mocharc.js` | ts-node/register, spec reporter, no-experimental-strip-types | +| `.nycrc` | 40% coverage threshold, excludes cli.js/cliold.js + appInsightsTransport.ts | + +**Test file naming:** `*.test.ts` (consistent with all v4 packages). + +**Test layout:** + +``` +tests/ + unit/ + error.test.ts — MissingRequiredOptionError, InvalidChoiceError, etc. + handler.test.ts — wrapHandler() telemetry start/end/error, flush on error, exitCode + output.test.ts — colorize() TextType, printResult() JSON/table/empty + commands.test.ts — buildProgram() subcommands, global options, version flag + context.test.ts — createCliContext(): projectPath, correlationId, wiring + factory.test.ts — buildNewCommands(): category subcommands, slug mapping, template + options, --language omission, question→option mapping, extensibility proof + actions.test.ts — listTemplatesAction, createProjectAction, envListAction, envAddAction, + envResetAction (uses real core-next env operations on temp dirs) + da-actions.test.ts — addActionAction, addCapabilityAction, addAuthConfigAction, + setSensitivityLabelAction, m365SideloadAction (error paths + signatures) + integration/ + commandExecution.test.ts — --version parse, command groups, global options, error class integration + coreNextIntegration.test.ts — Real core-next ops through Commander: env list/add/reset, + list templates, new command tree inspection (da/bot subcommands) + addCommands.test.ts — add/set/m365 command tree inspection, real DA ops via parseAsync + (web-search capability, sensitivityLabel), required option verification +``` + +**Test commands:** + +```bash +cd packages/cli-next +npm run test:unit # 78 unit tests (with NYC coverage) +npm run test:integration # 62 integration tests +npm run test # Alias for test:unit +``` + +**Testing the `wrapHandler()` pattern:** + +```typescript +import * as sinon from "sinon"; +import { cliTelemetry } from "../../src/telemetry"; +import { wrapHandler } from "../../src/handler"; + +const sandbox = sinon.createSandbox(); +sandbox.stub(cliTelemetry, "sendEvent"); +sandbox.stub(cliTelemetry, "sendErrorEvent"); +sandbox.stub(cliTelemetry, "flush").resolves(); + +// Create a mock Commander Command for testing +const cmd = new Command("test-cmd"); +cmd.setOptionValue("output", "text"); +const wrapped = wrapHandler("mycommand", handler); +await wrapped(cmd); +``` diff --git a/.github/instructions/codebase.instructions.md b/.github/instructions/codebase.instructions.md new file mode 100644 index 00000000000..6e2605f7a5c --- /dev/null +++ b/.github/instructions/codebase.instructions.md @@ -0,0 +1,343 @@ +--- +description: "Use when editing, creating, or reviewing code in any package: manifest, api, fx-core, core-next, server, vscode-extension, cli, cli-next, or templates. Covers monorepo conventions, error handling, testing, formatting, imports, and commit rules." +applyTo: "packages/**/*.ts, templates/**/*.ts" +--- + +# Microsoft 365 Agents Toolkit — Codebase Conventions + +## Monorepo Structure + +PNPM workspaces + Lerna. Dependency flow: + +``` +# Current (v3) +api → manifest → fx-core → cli / vscode-extension / server + → templates (build output copied into fx-core) + +# Next generation (v4) — gated by TEAMSFX_V4_CORE feature flag +core-next → cli-next + → (future) vscode-extension / server +``` + +Changing `api` impacts all current downstream packages — rebuild before testing. +Changing `core-next` impacts `cli-next` and future v4 consumers. + +## Package Roles + +| Package | npm name | Role | +|---------|----------|------| +| `packages/api` | `@microsoft/teamsfx-api` | Contracts, interfaces, error types — the public API surface | +| `packages/manifest` | `@microsoft/app-manifest` | Manifest types, JSON schema validation, converters | +| `packages/fx-core` | `@microsoft/teamsfx-core` | Core engine — generators, drivers, coordinators | +| `packages/server` | `@microsoft/teamsfx-server` | JSON-RPC server bridge to fx-core | +| `packages/vscode-extension` | `ms-teams-vscode-extension` | VS Code extension UI, handlers, tree views | +| `packages/cli` | `@microsoft/m365agentstoolkit-cli` | CLI tool (`atk` binary) — current v1.x | +| `packages/core-next` | `@microsoft/teamsfx-core` v4.0.0 | **Next-gen** core engine — merged API contracts, AtkContext, Operation pipeline, TemplateRegistry + **E2E-verified** scaffold system (auto-fallback, per-template filter/prefix-strip, `convertToLangKey()`, bundled fallback ZIPs) + 43 built-in descriptors, question model (buildQuestionTree + traverseQuestionTree + createProjectInteractive), DriverRegistry + createDriver factory + 21 built-in drivers, service clients (TeamsDevPortal, GraphApi, Azure ARM, M365 PackageService), DA module (knowledge/actions/auth/capabilities), lifecycle engine + operations (provisionOp, deployOp, publishOp with composable prerequisites, driver introspection, progress), project creation, environment management, teamsApp operations | +| `packages/cli-next` | `@microsoft/m365agentstoolkit-cli` v4.0.0 | **Next-gen** CLI — Commander.js-based, registry-driven command factory, action layer wired to core-next operations (createProject, provision, deploy, publish, env, teamsapp validate/package, list templates) | +| `templates/` | — | Scaffolding templates (TS/JS/Python/C#), built into fx-core | + +## Change Placement + +| Change Type | Package | +|-------------|---------| +| New interface or contract | `packages/api` | +| App manifest schema or validation | `packages/manifest` | +| New command, generator, or driver | `packages/fx-core` | +| CLI command handling or prompts (v3) | `packages/cli` | +| New v4 CLI action function (createProject, lifecycle, env, teamsapp) | `packages/cli-next/src/actions/` | +| New v4 CLI command factory or slug mapping | `packages/cli-next/src/commands/factory.ts` | +| New v4 core interface or contract | `packages/core-next/src/api/` | +| New v4 operation, driver, or template descriptor | `packages/core-next` | +| New v4 built-in driver implementation | `packages/core-next/src/drivers/builtin/` | +| New v4 service client (TeamsDevPortal, GraphApi, Azure ARM) | `packages/core-next/src/clients/` | +| New v4 Declarative Agent feature (knowledge, action, auth) | `packages/core-next/src/declarativeAgent/` | +| New v4 lifecycle, YAML action, or lifecycle operation | `packages/core-next/src/lifecycle/` | +| New v4 scaffold or template registration | `packages/core-next/src/templates/` | +| New v4 question factory or traversal logic | `packages/core-next/src/questions/` | +| New v4 project creation or environment operation | `packages/core-next/src/project/` or `src/environment/` | +| New v4 Teams app packaging or publishing | `packages/core-next/src/teamsApp/` | +| New v4 telemetry helper or correlation | `packages/core-next/src/telemetry/` | +| New v4 CLI telemetry transport or sanitisation | `packages/cli-next/src/telemetry/` | +| New v4 secret masking rule | `packages/core-next/src/secretMasker/` | +| New v4 feature flag | `packages/core-next/src/featureFlags/` | +| New v4 localization string or bundle | `packages/core-next/src/localization/` | +| New v4 HTTP client helper or interceptor | `packages/core-next/src/http/` | +| VS Code UI, commands, tree views | `packages/vscode-extension` | +| New project template | `templates/` + metadata in `fx-core` | + +## File Headers + +Every `.ts` source file must start with: + +```typescript +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + */ +``` + +## Error Handling + +Use the `Result` pattern from `neverthrow` (re-exported by `@microsoft/teamsfx-api`): + +- `UserError` — recoverable, user-fixable (bad input, missing config) +- `SystemError` — infrastructure/service failures +- Return `ok(value)` or `err(new UserError({...}))`, never throw for expected failures +- Always set `source` to the component/plugin name +- User-facing messages: `getLocalizedString("key", ...params)`, never raw strings +- Propagate errors with context — wrap inner errors via `innerError` property +- Keep error names stable (they appear in telemetry); change `message` for clarity, not `name` + +```typescript +// Good +return err(new UserError({ + source: "AppStudioPlugin", + name: "ManifestValidationError", + message: getLocalizedString("plugins.appStudio.validationFailed", details), + helpLink: "https://aka.ms/...", +})); + +// Bad — throwing, raw string, no source +throw new Error("manifest validation failed"); +``` + +## Type Safety + +- Enable `strict: true` in `tsconfig.json` — all packages already do this +- TypeScript 6.0 (`~6.0.0`) in core-next and cli-next; `moduleResolution: "bundler"` (the `node10` deprecation is resolved) +- Prefer `unknown` over `any` for untyped external data; narrow with type guards +- Use discriminated unions over type assertions when branching on shape +- Avoid `as` casts — use type predicates or `satisfies` operator instead +- Export interfaces from `api` package for cross-package contracts; concrete types stay internal + +```typescript +// Good — type predicate +function isUserError(e: FxError): e is UserError { + return e instanceof UserError; +} + +// Bad — unsafe cast +const ue = e as UserError; +``` + +## Formatting (Prettier) + +- Double quotes, semicolons, 2-space indent +- 100-character print width, LF line endings +- Config: `packages/prettier-config` + +## Linting (ESLint) + +All packages use ESLint flat config (`eslint.config.mjs`) extending `../eslint-plugin-teamsfx/config/shared.mjs` plus `header.mjs`. +Packages may also add `promise.mjs` and/or `type.mjs` overrides. + +| Package | ESLint configs used | Special rules | +|---------|--------------------|--------------| +| fx-core | shared + header + promise + type | — | +| cli | shared + header + promise | — | +| core-next | shared + header + promise | `import-x/no-unresolved: off` | +| cli-next | shared + header | `import-x/no-unresolved: off` | + +`import-x/no-unresolved` is disabled in core-next and cli-next because the node resolver +cannot follow pnpm `workspace:*` links. TypeScript compilation catches real import errors. + +Key rules: +- License header required on every `.ts` file +- No floating promises — `await` or `return` every promise +- No hardcoded secrets (`no-secrets` rule, tolerance 4.5 entropy) +- No import cycles +- Unused variables must be prefixed with `_` (e.g., `_unused`, `_err`) — enforced by `@typescript-eslint/no-unused-vars` with `argsIgnorePattern: "^_"` + +Each package also has `tsconfig.eslint.json` (extends `tsconfig.json`, `noEmit: true`, includes `src` + `tests`) and `.prettierrc.js` (extends `../prettier-config`). + +## Naming Conventions + +| Element | Style | Examples | +|---------|-------|---------| +| Classes / Interfaces | PascalCase | `FxCore`, `UserError`, `LogProvider` | +| Functions / methods | camelCase | `loadFromPath`, `askSubscription` | +| Constants | SCREAMING_SNAKE_CASE | `AppStudioScopes`, `SharePointAppId` | +| Enums | PascalCase with PascalCase members | `FeatureFlagName.MultiEnv` | +| Files | camelCase or PascalCase matching main export | `FxCore.ts`, `featureFlags.ts` | +| Test files | `.test.ts` | `FxCore.test.ts`, `error.test.ts` | + +## Imports + +- Relative paths (`./`, `../`) — no path aliases +- Order: external packages → workspace packages → relative imports +- Use workspace protocol references (`workspace:*`) in `package.json` +- Use `import type { ... }` for type-only imports to avoid runtime overhead + +```typescript +// Good +import type { FxError } from "@microsoft/teamsfx-api"; +import { ok, err } from "neverthrow"; +import { featureFlagManager } from "../common/featureFlags"; + +// Bad — unordered, value import for types +import { featureFlagManager } from "../common/featureFlags"; +import { FxError } from "@microsoft/teamsfx-api"; +import { ok } from "neverthrow"; +``` + +## Testing + +- **Framework:** Mocha + Chai + Sinon (all packages) +- **Coverage:** NYC (Istanbul) with `@istanbuljs/nyc-config-typescript` +- **Test file naming:** `*.test.ts` (all packages: fx-core, core-next, cli-next) +- **Test location:** `tests/` directory mirroring `src/` structure +- **Test types:** `tests/unit/` for unit tests, `tests/integration/` for integration tests +- **Test counts:** core-next: 492 unit + 24 integration; cli-next: 78 unit + 62 integration +- **Lint status:** core-next: 154 warnings (all `no-explicit-any`); cli-next: 19 warnings (all `no-explicit-any`) +- Every new feature or bug fix must include tests +- Run: `cd packages/ && npm run test:unit` + +### Test Config Files (per package) + +| File | Purpose | +|------|---------| +| `.mocharc.js` | Mocha settings: `ts-node/register`, spec reporter, `no-experimental-strip-types` | +| `.nycrc` | NYC coverage config: extends `@istanbuljs/nyc-config-typescript`, coverage thresholds | + +### Test Scripts + +```bash +npm run build # TypeScript compile + postbuild (eslint --fix + prettier --write) +npm run test:unit # Unit tests only (with NYC coverage) +npm run test:integration # Integration tests only (no coverage) +npm run test # Default — same as test:unit +npm run lint # ESLint check (0 errors required; warnings acceptable) +npm run format # Prettier auto-format +npm run format:check # Prettier check (CI gate) +``` + +> **`postbuild` hook:** Both core-next and cli-next run `eslint --fix` + `prettier --write` +> automatically after `build`. No separate format step needed after building. + +### Test Structure + +```typescript +describe("ComponentName", () => { + // Group by method or behavior + describe("methodName", () => { + afterEach(() => { + sinon.restore(); // Always clean up stubs + }); + + it("should return ok when input is valid", async () => { + // Arrange + const input = { ... }; + // Act + const result = await component.method(input); + // Assert + expect(result.isOk()).to.be.true; + }); + + it("should return UserError when config is missing", async () => { + const result = await component.method({}); + expect(result.isErr()).to.be.true; + expect(result._unsafeUnwrapErr().name).to.equal("MissingConfigError"); + }); + }); +}); +``` + +### core-next Test Helper + +`tests/unit/testHelper.ts` exports `createMockContext()` — creates a fully-stubbed `AtkContext` +with sinon stubs for `telemetry`, `logger`, `ui`, and `auth`. Use it in every core-next test. + +### Integration Tests + +Integration tests verify cross-module interactions without external services: +- **core-next:** Operation pipeline → telemetry sequence, cross-cutting module composition +- **cli-next:** Commander program parsing, error class integration, command group registration + +### Test Best Practices + +- Use `sinon.stub()` / `sinon.sandbox` for external dependencies; always `sinon.restore()` in `afterEach` +- Test both `ok` and `err` paths for every `Result`-returning function +- Avoid testing implementation details — test behavior and outputs +- Mock I/O boundaries (file system, HTTP, Azure SDK clients), not internal logic +- Name tests as `should when ` + +## Async Code + +- Always `async`/`await` — no raw `.then()` chains +- No floating promises — every promise must be awaited or returned +- Use `Promise.all()` for independent concurrent operations, not sequential awaits +- Handle cleanup in `finally` blocks when acquiring resources (file handles, connections) + +## Localization + +- User-facing strings: `getLocalizedString("teamsfx.key")` from `common/localizeUtils` +- Default English strings via `getDefaultString("key")` +- Translation files in `Localize/loc/` +- Never concatenate translated strings — use parameterized keys: `getLocalizedString("key", param1, param2)` + +## Security + +- Never log secrets, tokens, or credentials — use `maskSecret()` from `common/stringUtils` +- Validate all external input at system boundaries (CLI args, API responses, file contents) +- Use `validator` library for URL/string validation; no hand-rolled regex for security checks +- Prefer `fs-extra` over raw `fs` for atomic file operations + +## Telemetry + +- Every public API entry point should emit start/end telemetry events +- Include `correlationId` from `common/correlator` for distributed tracing +- Error telemetry: include `error.source`, `error.name`, `error.message` — never include PII +- Use `TelemetryReporter` interface for telemetry collection + +## Feature Flags + +- Use `featureFlagManager` from `common/featureFlags` to check flags +- Gate new behavior behind flags for safe rollout; keep the old code path functional +- Never hardcode flag values — always read from `featureFlagManager` + +## Commits (Conventional Commits) + +Format: `type(scope): subject` + +Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `revert`, `perf`, `ci`, `build` + +``` +feat(fx-core): add new generator for custom agents +fix(cli): resolve crash on missing config file +test(api): add contract tests for new interface +``` + +## Build Commands + +```bash +npm run setup # Full monorepo install + build +npm run setup:cli # CLI + dependencies +npm run setup:vsc # VS Code extension + dependencies +pnpm --filter @microsoft/teamsfx-core run build # Single package +npm run watch # Watch mode (all packages) + +# v4 packages +cd packages/core-next && npm run build # Build core-next +cd packages/core-next && npm run test:unit # 492 unit tests +cd packages/core-next && npm run test:integration # 24 integration tests +cd packages/cli-next && npm run build # Build cli-next +cd packages/cli-next && npm run test:unit # 78 unit tests +cd packages/cli-next && npm run test:integration # 62 integration tests +``` + +## Templates + +- Organized by platform and language: `vsc/(ts|js|python)/`, `vs/csharp/` +- Dynamic files use `.tpl` suffix with Mustache syntax +- Every template includes: `appPackage/`, `infra/`, `m365agents.yml`, `env/` +- Build output is distributed into `packages/fx-core/templates` +- Register new templates in generator metadata and `generatorProvider.ts` + +## Common Pitfalls + +- **Forgetting to rebuild dependencies:** Change `api` → rebuild before testing `fx-core` +- **Floating promises:** ESLint catches these — fix, don't suppress +- **Wrong commit format:** commitlint rejects non-conventional messages +- **Raw error strings:** Always use `getLocalizedString()` for user-facing messages +- **Wrong error type:** `UserError` for input/config issues, `SystemError` for infra failures +- **Lock file conflicts:** Run `pnpm install` to regenerate — never edit lock files manually diff --git a/.github/instructions/features.instructions.md b/.github/instructions/features.instructions.md new file mode 100644 index 00000000000..c83fec19f62 --- /dev/null +++ b/.github/instructions/features.instructions.md @@ -0,0 +1,180 @@ +--- +description: "Use when exploring toolkit capabilities, adding new templates, or writing integration tests. Provides the canonical list of features and templates with rich metadata." +applyTo: "packages/fx-core/**/*.ts, templates/**, .dev/features.json" +--- + +# Feature Registry — Toolkit Capabilities Reference + +The **single source of truth** for all testable features lives in two files: + +``` +.dev/features.json ← Human/machine readable (edit this) +packages/fx-core/tests/integration/featureRegistry.ts ← Typed wrapper (loads JSON, adds helpers) +``` + +> **v4 Note:** `packages/core-next/src/templates/` contains a `TemplateRegistry` with +> `TemplateDescriptor` records. **43 descriptors** are registered across 8 files in +> `src/templates/descriptors/`: DA (11), Bot (8), Tab (5), AI Agent (5), Engine Agent (4), +> Connector (1), Message Extension (6), OpenAPI (3). Registered via `registerBuiltinTemplates()`. +> Descriptors support `questions?: QuestionSpec[]` for template-specific prompts (e.g., LLM +> provider selection for AI agents, graph connector config for connectors). +> OpenAPI descriptors use `makeOpenApiScaffoldFn()` with a pluggable `SpecParserAdapter`. +> +> **Scaffold pipeline is E2E-verified.** The scaffold system now includes: +> - `resolveFallbackDir()` — auto-resolves local fallback ZIPs (explicit param → `TEMPLATE_FALLBACK_DIR` env → bundled `templates/fallback/`) +> - Auto-filter by `{templateName}/` prefix in language-level ZIPs + prefix stripping before file write +> - `convertToLangKey()` — maps `\"typescript\"` → `\"ts\"`, `\"javascript\"` → `\"js\"`, etc. +> - `getTemplatesFolder()` in `src/folder.ts` — resolves bundled templates dir from compiled output +> - Bundled fallback ZIPs shipped in `packages/core-next/templates/fallback/` (common, ts, js, python, csharp) +> - **9/9 E2E scaffolds verified** via cli-next: bot (TS/JS/Python), DA basic, AI chat, CEA basic, CEA weather (Python), tab basic, connector graph +> +> **Template name constants must match actual folder names** in `templates/vsc/{lang}/`, not legacy +> display names. Example: `DATemplateNames.Basic = \"declarative-agent-basic\"` (not `\"copilot-gpt-basic\"`). +> +> The question model in `src/questions/` provides `buildQuestionTree(registry)` to auto-generate +> `IQTreeNode` trees from registry metadata, and `traverseQuestionTree(tree, ui, inputs)` for +> interactive traversal with back navigation. `createProjectInteractive(ctx, inputs)` combines +> both for question-driven project creation. +> +> The driver system has a `createDriver()` factory and **21 built-in driver implementations** +> registered via `registerBuiltinDrivers()`. +> Service clients in `src/clients/` provide authenticated access to Teams Developer Portal, +> Microsoft Graph (Entra ID), Azure ARM APIs, and M365 PackageService (sideloading). +> +> The lifecycle engine in `src/lifecycle/` provides: +> - **Parser + executor**: `parseProjectYaml()` → `executeLifecycle()` with optional `LifecycleProgress` callbacks +> - **Driver introspection**: `analyzeSteps()` determines M365/Azure prerequisites from step driver IDs +> - **Composable prerequisites**: `ensureM365Auth`, `ensureAzureAuth`, `ensureSubscription`, `ensureResourceGroup`, `confirmProvision`, `confirmDeploy` +> - **Operations**: `provisionOp`, `deployOp`, `publishOp` — complete orchestration via `defineOperation()` +> - **Progress**: `createProgressAdapter(ui)` bridges to platform progress bar; `silentProgress` for CI +> +> **v4 Testing:** Both `packages/core-next/` and `packages/cli-next/` have full test +> infrastructure (Mocha + Chai + Sinon + NYC). core-next has **492 unit tests**; +> cli-next has **78 unit + 62 integration tests**; core-next has **24 integration tests**. +> Plus **9 E2E scaffold tests** verified via cli-next. +> Run with `npm run test:unit` / `npm run test:integration`. +> CI: `.github/workflows/ci-next.yml` (build → lint, format-check, unit-test, integration-test). +> ESLint flat config with `shared` + `header`; Prettier shared config; 80% coverage gate. +> +> **v4 Lifecycle coverage:** 38 of 39 templates have all drivers registered for full +> provision/deploy/publish support. The only gap is `typeSpec/compile` for the +> `declarative-agent-typespec` template. Drivers added in this pass: +> - `teamsApp/update` — alias of configure, referenced by all templates +> - `teamsApp/extendToM365` — sideloads to M365 ecosystem (V1 classic, V2 DA) +> - `cli/runNpmCommand` — runs npm install/build for deploy lifecycle +> Auth providers are real MSAL-based implementations (not stubs). +> +> The `atk new` command tree is generated from `TemplateRegistry` via `buildNewCommands()` in +> `src/commands/factory.ts`. Adding a new `TemplateDescriptor` to the registry automatically +> creates a new CLI subcommand (e.g., `atk new da