mirror of
https://github.com/Tencent/teamai-cli.git
synced 2026-10-02 03:14:40 +08:00
fix(init): preserve additional roles selected at the prompt (#765)
This commit is contained in:
@@ -171,6 +171,8 @@ If the repo has role-based skills enabled (i.e. `manifest/roles.yaml` exists), `
|
||||
- `primaryRole`: the target namespace for skill sync and push by default
|
||||
- `additionalRoles`: additional skill namespaces to sync
|
||||
|
||||
At the role prompt, enter one or more comma-separated role numbers. The first number becomes `primaryRole` and the remaining numbers become `additionalRoles` (for example, `1,3`).
|
||||
|
||||
You can also skip the interactive prompts via CLI flags for a fully non-interactive init (suitable for CI/CD or AI agents):
|
||||
|
||||
```bash
|
||||
|
||||
@@ -165,6 +165,8 @@ teamai init https://github.com/yourorg/yourrepo
|
||||
- `primaryRole`:默认 skill 同步和推送的目标 namespace
|
||||
- `additionalRoles`:额外需要同步的 skill namespace
|
||||
|
||||
角色提示中可以输入一个或多个用逗号分隔的角色编号。第一个编号会保存为 `primaryRole`,后续编号会保存为 `additionalRoles`(例如 `1,3`)。
|
||||
|
||||
也可以通过 CLI 参数跳过交互,实现完全非交互式初始化(适合 CI/CD 或 AI agent):
|
||||
|
||||
```bash
|
||||
|
||||
@@ -187,9 +187,11 @@ If the repo does not exist yet, `init` offers to create it — accept the prompt
|
||||
- If `init` detects an unknown GitLab host, it stops and asks you to set
|
||||
`GITLAB_URL` + `GITLAB_TOKEN`, then retry.
|
||||
|
||||
If the repo has roles enabled, `init` may ask for a primary role — pick one with
|
||||
the user, or pass `--role <id>` for a non-interactive run. Without a terminal
|
||||
(or with `CI` / `TEAMAI_NONINTERACTIVE` set) `init` never waits: a provider that
|
||||
If the repo has roles enabled, `init` asks for one or more comma-separated role
|
||||
numbers when running interactively. The first number is the primary role and the
|
||||
remaining numbers become additional roles; pass `--role <id>` for a
|
||||
non-interactive run when only a primary role is needed. Without a terminal (or
|
||||
with `CI` / `TEAMAI_NONINTERACTIVE` set) `init` never waits: a provider that
|
||||
would need a browser login fails at once and names the credential to prepare —
|
||||
a token for GitHub / CNB / GitLab / GitCode, and for TGit a prior `gf auth
|
||||
login` run in an interactive shell (see Step 3).
|
||||
|
||||
@@ -561,6 +561,58 @@ describe('init', () => {
|
||||
resourceProfileVersion: 1,
|
||||
}));
|
||||
});
|
||||
|
||||
it('persists later selections as additional roles', async () => {
|
||||
let cloneDone = false;
|
||||
pathExistsFn = (p: string) => {
|
||||
if (p === localPath) return cloneDone;
|
||||
if (p === path.join(localPath, 'members', 'testuser.yaml')) return false;
|
||||
return false;
|
||||
};
|
||||
|
||||
mockGfRepoClone.mockImplementation(() => {
|
||||
cloneDone = true;
|
||||
});
|
||||
|
||||
const mockedLoadTeamConfig = vi.mocked(await import('../config.js')).loadTeamConfig;
|
||||
mockedLoadTeamConfig
|
||||
.mockResolvedValueOnce({
|
||||
team: 'my-team',
|
||||
repo: 'https://git.woa.com/HyperAI/teamai-test.git',
|
||||
provider: 'tgit',
|
||||
reviewers: [],
|
||||
sharing: {
|
||||
skills: {},
|
||||
rules: { enforced: [] },
|
||||
docs: { localDir: '~/.teamai/docs' },
|
||||
env: { injectShellProfile: true },
|
||||
},
|
||||
toolPaths: {},
|
||||
} as never)
|
||||
.mockResolvedValueOnce({
|
||||
team: 'my-team',
|
||||
repo: 'https://git.woa.com/HyperAI/teamai-test.git',
|
||||
provider: 'tgit',
|
||||
reviewers: [],
|
||||
sharing: {
|
||||
skills: {},
|
||||
rules: { enforced: [] },
|
||||
docs: { localDir: '~/.teamai/docs' },
|
||||
env: { injectShellProfile: true },
|
||||
},
|
||||
toolPaths: {},
|
||||
} as never);
|
||||
|
||||
questionAnswers = ['n', '1,3'];
|
||||
|
||||
await init({ repo: 'https://git.woa.com/HyperAI/teamai-test.git', scope: 'user' });
|
||||
|
||||
expect(saveLocalConfig).toHaveBeenCalledWith(expect.objectContaining({
|
||||
primaryRole: 'hai',
|
||||
additionalRoles: ['thpc'],
|
||||
resourceProfileVersion: 1,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('deploys built-in skills after init', () => {
|
||||
|
||||
+4
-3
@@ -108,13 +108,14 @@ async function promptForRoleProfile(
|
||||
log.info(` ${index + 1}. ${label}`);
|
||||
});
|
||||
|
||||
const primaryAnswer = await askQuestion('Primary role (number): ').catch(() => {
|
||||
const primaryAnswer = await askQuestion('Primary role (number or comma-separated numbers, primary first): ').catch(() => {
|
||||
throw new Error(
|
||||
'This team repo has several roles and there is no terminal to pick one. ' +
|
||||
`Pass --role <id> (one of: ${listRoleIds(manifest).join(', ')}).`,
|
||||
);
|
||||
});
|
||||
const [primaryIndex] = parseRoleSelection(primaryAnswer, manifest.roles.length);
|
||||
const selectedIndexes = parseRoleSelection(primaryAnswer, manifest.roles.length);
|
||||
const [primaryIndex, ...additionalIndexes] = selectedIndexes;
|
||||
if (!primaryIndex) {
|
||||
throw new NoRoleSelectedError('A primary role is required.');
|
||||
}
|
||||
@@ -123,7 +124,7 @@ async function promptForRoleProfile(
|
||||
|
||||
return {
|
||||
primaryRole: primaryRole.id,
|
||||
additionalRoles: [],
|
||||
additionalRoles: additionalIndexes.map((index) => manifest.roles[index - 1].id),
|
||||
resourceProfileVersion: manifest.version,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user