Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@ jobs:
token: ${{ steps.generate-token.outputs.token }}
prerelease: true
tag: "[email protected]"
artifacts: ${{ github.workspace }}/templates/build/*.zip
artifacts: ${{ github.workspace }}/templates/build/fallback/*.zip,${{ github.workspace }}/templates/build/metadata.zip
allowUpdates: true
removeArtifacts: true

- name: Create Templates Stable Release
if: ${{ contains(steps.version-change.outputs.CHANGED, 'templates@') && github.event_name == 'workflow_dispatch' && github.event.inputs.preid == 'stable' }}
uses: ncipollo/[email protected]
with:
artifacts: ${{ github.workspace }}/templates/build/*.zip
artifacts: ${{ github.workspace }}/templates/build/fallback/*.zip,${{ github.workspace }}/templates/build/metadata.zip
name: "Release for ${{ steps.version-change.outputs.TEMPLATE_VERSION }}"
token: ${{ steps.generate-token.outputs.token }}
tag: ${{ steps.version-change.outputs.TEMPLATE_VERSION }}
Expand Down
5 changes: 5 additions & 0 deletions Localize/LocProject.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
"SourceFile": "packages\\fx-core\\resource\\package.nls.json",
"CopyOption": "LangIDOnName",
"OutputPath": "packages\\fx-core\\resource"
},
{
"SourceFile": "templates\\src\\ui\\resource\\package.nls.json",
"CopyOption": "LangIDOnName",
"OutputPath": "templates\\src\\ui\\resource"
}
]
}
Expand Down
43 changes: 17 additions & 26 deletions packages/cli/src/commands/models/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
CLIContext,
err,
ok,
OptionItem,
Platform,
} from "@microsoft/teamsfx-api";
import {
Expand All @@ -15,12 +14,7 @@ import {
CreateProjectOptions,
featureFlagManager,
FeatureFlags,
getProjectTypeByCapability,
getTeamsAppTypeByCapability,
getTeamsCapabilityByCapability,
isTdpTemplate,
MeArchitectureOptions,
QuestionNames,
} from "@microsoft/teamsfx-core";
import chalk from "chalk";
import { assign } from "lodash";
Expand All @@ -31,21 +25,13 @@ import { logger } from "../../commonlib/logger";
import { commands } from "../../resource";
import { TelemetryEvent, TelemetryProperty } from "../../telemetry/cliTelemetryEvents";
import { createSampleCommand } from "./createSample";
import { listAllCapabilities } from "./listTemplates";
import { listAllTemplates } from "./listTemplates";

function adjustOptions(options: CLICommandOption[]) {
for (const option of options) {
if (option.type === "string" && option.name === CliQuestionName.Capability) {
// use dynamic options for capability question
option.choices = listAllCapabilities().map((o) => o.id);
break;
}
}

for (const option of options) {
if (option.type === "string" && option.name === QuestionNames.MeArchitectureType.toString()) {
// use dynamic options for ME architecture question
option.choices = MeArchitectureOptions.all().map((o: OptionItem) => o.id);
option.choices = listAllTemplates().map((o) => o.name);
break;
}
}
Expand All @@ -60,12 +46,12 @@ export function getCreateCommand(): CLICommand {
options: [...adjustOptions(CreateProjectOptions)],
examples: [
{
command: `${process.env.TEAMSFX_CLI_BIN_NAME} new -c notification -t timer-functions -l typescript -n myapp -i false`,
description: "Create a new timer triggered notification bot",
command: `${process.env.TEAMSFX_CLI_BIN_NAME} new -c copilot-gpt-basic -n myagent -i false`,
description: "Create a new declarative agent",
},
{
command: `${process.env.TEAMSFX_CLI_BIN_NAME} new -c tab-spfx -s import --spfx-folder <folder-path> -n myapp -i false`,
description: "Import an existing SharePoint Framework solution",
command: `${process.env.TEAMSFX_CLI_BIN_NAME} new -c basic-custom-engine-agent -l typescript -n mycea -i false`,
description: "Create a new basic custom engine agent",
},
],
commands: [createSampleCommand],
Expand All @@ -80,15 +66,20 @@ export function getCreateCommand(): CLICommand {
if (featureFlagManager.getBooleanValue(FeatureFlags.CLIDotNet)) {
// this feature is used in e2e test to scaffold VS project in non-interactive mode
inputs.platform = Platform.VS;
inputs["template-name"] = inputs.capabilities;
inputs["programming-language"] = "csharp";
} else {
// for non-interactive mode, we need to preset project-type from capability to make sure the question model works
const capability = inputs.capabilities as string;
const projectType = getProjectTypeByCapability(capability);
inputs["project-type"] = projectType as any;
const teamsAppType = getTeamsAppTypeByCapability(capability);
inputs["teams-app-type"] = teamsAppType;
const teamsCapability = getTeamsCapabilityByCapability(capability);
inputs["teams-capability"] = teamsCapability;
inputs["template-name"] = capability;
if (inputs["programming-language"] === undefined) {
// preset programming language if not specified
const templates = listAllTemplates();
const matched = templates.find((t) => t.name === capability);
if (matched) {
inputs["programming-language"] = matched.language as any;
}
}
}
}
const isTdp = isTdpTemplate(inputs);
Expand Down
144 changes: 61 additions & 83 deletions packages/cli/src/commands/models/listTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { CLICommand, ok, OptionItem } from "@microsoft/teamsfx-api";
import { CLICommand, ok, Platform } from "@microsoft/teamsfx-api";
import {
BotCapabilityOptions,
CustomEngineAgentOptions,
DACapabilityOptions,
featureFlagManager,
FeatureFlags,
MeCapabilityOptions,
OfficeAddinCapabilityOptions,
TabCapabilityOptions,
TeamsAgentCapabilityOptions,
VSCapabilityOptions,
getAllTemplatesOnPlatform,
} from "@microsoft/teamsfx-core";
import chalk from "chalk";
import Table from "cli-table3";
Expand All @@ -20,53 +13,35 @@ import { commands } from "../../resource";
import { TelemetryEvent } from "../../telemetry/cliTelemetryEvents";
import { ListFormatOption } from "../common";

export function listAllCapabilities(): OptionItem[] {
interface TemplateGroup {
name: string;
displayName: string;
description: string;
language: string;
}

export function listAllTemplates(): TemplateGroup[] {
let templates = getAllTemplatesOnPlatform(Platform.VSCode);
if (featureFlagManager.getBooleanValue(FeatureFlags.CLIDotNet)) {
// return all capabilities for .NET
return [
VSCapabilityOptions.empty(),
VSCapabilityOptions.declarativeAgent(),
TeamsAgentCapabilityOptions.basicChatbot(),
TeamsAgentCapabilityOptions.collaboratorAgent(),
TeamsAgentCapabilityOptions.customCopilotRag(),
// TeamsAgentCapabilityOptions.aiAgent(),
VSCapabilityOptions.weatherAgentBot(),
BotCapabilityOptions.basicBot(),
BotCapabilityOptions.notificationBot(),
BotCapabilityOptions.commandBot(),
BotCapabilityOptions.workflowBot(),
VSCapabilityOptions.nonSsoTab(),
VSCapabilityOptions.tab(),
MeCapabilityOptions.m365SearchMe(),
MeCapabilityOptions.collectFormMe(),
VSCapabilityOptions.SearchMeVS(),
MeCapabilityOptions.linkUnfurling(),
];
templates = getAllTemplatesOnPlatform(Platform.VS);
}
return [
DACapabilityOptions.declarativeAgent(),
CustomEngineAgentOptions.basicCustomEngineAgent(),
CustomEngineAgentOptions.weatherAgent(),
TeamsAgentCapabilityOptions.basicChatbot(),
TeamsAgentCapabilityOptions.customCopilotRag(),
TeamsAgentCapabilityOptions.collaboratorAgent(),
// TeamsAgentCapabilityOptions.aiAgent(),
BotCapabilityOptions.basicBot(),
// BotCapabilityOptions.notificationBot(),
// BotCapabilityOptions.commandBot(),
// BotCapabilityOptions.workflowBot(),
TabCapabilityOptions.nonSsoTab(),
// TabCapabilityOptions.m365SsoLaunchPage(),
// TabCapabilityOptions.dashboardTab(),
// TabCapabilityOptions.SPFxTab(),
MeCapabilityOptions.basicMe(),
// MeCapabilityOptions.m365SearchMe(),
// MeCapabilityOptions.collectFormMe(),
// MeCapabilityOptions.linkUnfurling(),
OfficeAddinCapabilityOptions.wxpTaskPane(),
OfficeAddinCapabilityOptions.excelCFShortcut(),
OfficeAddinCapabilityOptions.outlookTaskPane(),
];

// Group by template name, ignoring programming language
const groupedTemplates = new Map<string, TemplateGroup>();

templates.forEach((template) => {
if (!groupedTemplates.has(template.name)) {
const templateWithDisplay = template as typeof template & { displayName?: string };
groupedTemplates.set(template.name, {
name: template.name,
displayName: templateWithDisplay.displayName || template.name,
description: template.description,
language: template.language,
});
}
});

return Array.from(groupedTemplates.values());
}

export const listTemplatesCommand: CLICommand = {
Expand All @@ -76,11 +51,12 @@ export const listTemplatesCommand: CLICommand = {
defaultInteractiveOption: false,
handler: (ctx) => {
const format = ctx.optionValues.format;
const templates = listAllTemplates();
let result;
if (format === "table") {
result = jsonToTable(listAllCapabilities());
result = jsonToTable(templates);
} else {
result = JSON.stringify(listAllCapabilities(), null, 2);
result = JSON.stringify(templates, null, 2);
}
logger.info(result);
return ok(undefined);
Expand All @@ -90,45 +66,47 @@ export const listTemplatesCommand: CLICommand = {
},
};

function jsonToTable(capabilities: OptionItem[]): string {
let maxUrlLength = 0;
function jsonToTable(templates: TemplateGroup[]): string {
let maxIdLength = 0;
let maxLabelLength = 0;
capabilities.forEach((item) => {
if (item.data && (item.data as string).length > maxUrlLength) {
maxUrlLength = (item.data as string).length;
let maxNameLength = 0;
let maxDescriptionLength = 0;
templates.forEach((template) => {
if (template.name.length > maxIdLength) {
maxIdLength = template.name.length;
}
if (("id: " + item.id).length > maxIdLength) {
maxIdLength = ("id: " + item.id).length;
if (template.displayName.length > maxNameLength) {
maxNameLength = template.displayName.length;
}
if (item.label.length > maxLabelLength) {
maxLabelLength = item.label.length;
if (template.description.length > maxDescriptionLength) {
maxDescriptionLength = template.description.length;
}
});
maxUrlLength += 2;
maxIdLength += 2;
maxLabelLength += 2;

const col1Length = Math.max(maxIdLength, maxLabelLength);

maxUrlLength = Math.max(80, maxUrlLength);
maxNameLength += 2;
maxDescriptionLength += 2;

const terminalWidth = process.stdout.isTTY ? process.stdout.columns : 80;
const idColWidth = Math.max(15, maxIdLength);
const nameColWidth = Math.max(20, maxNameLength);
const descColWidth = Math.min(
maxDescriptionLength,
terminalWidth - idColWidth - nameColWidth - 4
);

const table = new Table({
head: [chalk.cyanBright("Template"), chalk.cyanBright("Description")],
colAligns: ["left", "left"],
colWidths: [col1Length, Math.min(maxUrlLength, terminalWidth - col1Length - 3)],
head: [
chalk.cyanBright("Capability(Id)"),
chalk.cyanBright("Name"),
chalk.cyanBright("Description"),
],
colAligns: ["left", "left", "left"],
colWidths: [idColWidth, nameColWidth, descColWidth],
wordWrap: true,
});
capabilities.forEach((item) => {
const row = [item.label + chalk.gray("\nid: " + item.id)];
row.push(
chalk.gray([item.description, item.detail].filter((i) => !!i).join(". ")) +
"\n" +
(item.data ? chalk.underline.blue(item.data) : "")
);
table.push(row);

templates.forEach((template) => {
table.push([template.name, template.displayName, template.description]);
});

return table.toString();
}
2 changes: 2 additions & 0 deletions packages/fx-core/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ coverage
.fx
templates/**/*.zip
tests/**/TeamsSPFxApp.zip
resource/templates
tsconfig.tsbuildinfo
2 changes: 1 addition & 1 deletion packages/fx-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"test:metadataUtil": "nyc mocha \"tests/component/util/metadataUtil.test.ts\"",
"test:migrate": "nyc mocha \"tests/component/migrate.test.ts\"",
"test:retry": "npx mocha \"tests/core/middleware/retry.test.ts\"",
"clean": "rm -rf build",
"clean": "rimraf -rf build",
"prebuild": "npm run gen:cli",
"build": "tsc -p ./ --incremental",
"lint:fix": "eslint --fix \"src/**/*.ts\" \"tests/**/*.ts\"",
Expand Down
23 changes: 0 additions & 23 deletions packages/fx-core/resource/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,6 @@
"core.createProjectQuestion.projectType.bot.detail": "Create instant, engaging chat experiences that automate tasks seamlessly",
"core.createProjectQuestion.projectType.teamsAgentsAndApps.label": "Teams Agents and Apps",
"core.createProjectQuestion.projectType.teamsAgentsAndApps.detail": "Create agent, chat bot, web pages or other apps in Microsoft Teams using Microsoft Teams SDK.",
"core.createProjectQuestion.projectType.teamsAgentsAndApps.title": "Teams Agent or App Using Microsoft Teams SDK",
"core.createProjectQuestion.projectType.bot.label": "Bot",
"core.createProjectQuestion.projectType.bot.title": "App Features Using a Bot",
"core.createProjectQuestion.projectType.messageExtension.copilotEnabled.detail": "Search and take actions from the text input box in Teams and Outlook",
Expand All @@ -340,16 +339,13 @@
"core.createProjectQuestion.projectType.copilotExtension.placeholder": "Select an option",
"core.createProjectQuestion.projectType.customCopilot.detail": "Build intelligent agent where you manage orchestration and provide your own LLM",
"core.createProjectQuestion.projectType.customCopilot.label": "Custom Engine Agent",
"core.createProjectQuestion.projectType.customCopilot.title": "App Features Using Microsoft 365 Agents SDK",
"core.createProjectQuestion.projectType.customCopilot.placeholder": "Select an option",
"core.createProjectQuestion.projectType.copilotHelp.label": "Don't know how to start? Use GitHub Copilot Chat",
"core.createProjectQuestion.projectType.copilotHelp.detail": "Chat with GitHub Copilot and get step-by-step instructions to develop your app or Microsoft 365 Copilot agent.",
"core.createProjectQuestion.projectType.copilotGroup.title": "Use GitHub Copilot",
"core.createProjectQuestion.projectType.createGroup.aiAgent": "Agents for Microsoft 365 Copilot",
"core.createProjectQuestion.projectType.createGroup.m365Apps": "Apps for Microsoft 365",
"core.createProjectQuestion.projectType.declarativeAgent.label": "Declarative Agent",
"core.createProjectQuestion.projectType.declarativeAgent.detail": "Create your own agent by declaring instructions, actions, & knowledge to suit your needs.",
"core.createProjectQuestion.teamsCapability.title": "Teams Capability",
"core.createProjectQuestion.title": "New Project",
"core.createProjectQuestion.capability.botMessageExtension.label": "Start with a Bot",
"core.createProjectQuestion.capability.botMessageExtension.detail": "Create a message extension using Agents SDK",
Expand All @@ -364,30 +360,11 @@
"core.createProjectQuestion.capability.searchOpenAPISpecQueryQuestion.placeholder": "Input text to search OpenAPI description document",
"core.createProjectQuestion.capability.selectOpenAPISpecQuestion.label": "Select OpenAPI Description Document",
"core.createProjectQuestion.capability.messageExtensionApiSpecOption.detail": "Create a message extension from your existing API",
"core.createProjectQuestion.capability.basicCustomEngineAgentOption.label": "Basic Custom Engine Agent",
"core.createProjectQuestion.capability.basicCustomEngineAgentOption.detail": "Intelligent agent that is built with Microsoft 365 Agents SDK and connects to your LLM",
"core.createProjectQuestion.capability.weatherAgentOption.label": "Weather Agent",
"core.createProjectQuestion.capability.weatherAgentOption.detail": "A weather forecast agent that is built with Microsoft 365 Agents SDK and LangChain",
"core.createProjectQuestion.capability.customCopilotBasicOption.label": "General Teams Agent",
"core.createProjectQuestion.capability.customCopilotBasicOption.detail": "Agent that chats with users in Teams built with Microsoft Teams SDK and connects to LLMs",
"core.createProjectQuestion.capability.customCopilotRagOption.label": "Teams Agent with Data",
"core.createProjectQuestion.capability.customCopilotRagOption.detail": "Agent that uses your content and knowledge to accurately answer domain-specific questions",
"core.createProjectQuestion.capability.teamsAgent.collaborator.label": "Teams Collaborator Agent",
"core.createProjectQuestion.capability.teamsAgent.collaborator.detail": "Agent that enhances collaboration in group chat, channels or meetings through summarization, task management, and conversation search",
"core.createProjectQuestion.capability.teamsAgent.others.label": "Other Teams Capabilities",
"core.createProjectQuestion.capability.teamsAgent.others.detail": "App that uses tab, bot and message extension",
"core.createProjectQuestion.capability.customCopilotAssistantOption.label": "Agent with API",
"core.createProjectQuestion.capability.customCopilotAssistantOption.detail": "Agent for Teams that can make decisions and perform actions based on LLM reasoning",
"core.createProjectQuestion.capability.customCopilotRagCustomizeOption.label": "Customize",
"core.createProjectQuestion.capability.customCopilotRagCustomizeOption.detail": "Decide how to load your data",
"core.createProjectQuestion.capability.customCopilotRagAzureAISearchOption.label": "Azure AI Search",
"core.createProjectQuestion.capability.customCopilotRagAzureAISearchOption.detail": "Load your data from Azure AI Search service",
"core.createProjectQuestion.capability.customCopilotRagCustomApiOption.label": "Custom API",
"core.createProjectQuestion.capability.customCopilotRagCustomApiOption.detail": "Load your data from custom APIs based on OpenAPI description document",
"core.createProjectQuestion.capability.customCopilotRagMicrosoft365Option.label": "Microsoft 365",
"core.createProjectQuestion.capability.customCopilotRagMicrosoft365Option.detail": "Load your data from Microsoft Graph and SharePoint",
"core.createProjectQuestion.capability.customCopilotRag.title": "Chat With Your Data",
"core.createProjectQuestion.capability.customCopilotRag.placeholder": "Select an option to load your data",
"core.createProjectQuestion.capability.customCopilotAssistantNewOption.label": "Build from Scratch",
"core.createProjectQuestion.capability.customCopilotAssistantNewOption.detail": "Build your own AI Agent from scratch using Microsoft Teams SDK",
"core.createProjectQuestion.capability.customCopilotAssistantAssistantsApiOption.label": "Build with Assistants API",
Expand Down
Loading
Loading