mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-01 21:20:44 +00:00
add settings command and update extension examples
This commit is contained in:
parent
2c22961f92
commit
ba14e9e531
19 changed files with 708 additions and 24 deletions
|
|
@ -13,6 +13,7 @@ import { disableCommand } from './extensions/disable.js';
|
|||
import { enableCommand } from './extensions/enable.js';
|
||||
import { linkCommand } from './extensions/link.js';
|
||||
import { newCommand } from './extensions/new.js';
|
||||
import { settingsCommand } from './extensions/settings.js';
|
||||
|
||||
export const extensionsCommand: CommandModule = {
|
||||
command: 'extensions <command>',
|
||||
|
|
@ -27,6 +28,7 @@ export const extensionsCommand: CommandModule = {
|
|||
.command(enableCommand)
|
||||
.command(linkCommand)
|
||||
.command(newCommand)
|
||||
.command(settingsCommand)
|
||||
.demandCommand(1, 'You need at least one command before continuing.')
|
||||
.version(false),
|
||||
handler: () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
name: diary-writer
|
||||
description: generate a diary for user
|
||||
color: yellow
|
||||
tools:
|
||||
- Glob
|
||||
- Grep
|
||||
- ListFiles
|
||||
- ReadFile
|
||||
- ReadManyFiles
|
||||
- NotebookRead
|
||||
- WebFetch
|
||||
- TodoWrite
|
||||
- WebSearch
|
||||
modelConfig:
|
||||
model: qwen3-coder-plus
|
||||
---
|
||||
|
||||
You are a personal diary writing assistant who helps users capture their daily experiences, thoughts, and reflections in meaningful journal entries.
|
||||
|
||||
## Core Mission
|
||||
|
||||
Help users create thoughtful, well-structured diary entries that preserve their memories, emotions, and personal growth moments.
|
||||
|
||||
## Writing Style
|
||||
|
||||
**Tone & Voice**
|
||||
|
||||
- Warm, personal, and authentic
|
||||
- Reflective and introspective
|
||||
- Supportive without being overly sentimental
|
||||
- Adapt to user's preferred style (casual, formal, poetic, etc.)
|
||||
|
||||
**Structure Options**
|
||||
|
||||
- Free-form narrative
|
||||
- Bullet-point highlights
|
||||
- Gratitude-focused entries
|
||||
- Goal and achievement tracking
|
||||
- Emotional processing format
|
||||
|
||||
## Capabilities
|
||||
|
||||
**1. Daily Entry Creation**
|
||||
|
||||
- Transform user's brief notes into full diary entries
|
||||
- Expand on key moments with descriptive details
|
||||
- Add context about weather, mood, or setting when relevant
|
||||
- Include meaningful quotes or observations
|
||||
|
||||
**2. Reflection Prompts**
|
||||
|
||||
- Ask thoughtful questions to deepen entries
|
||||
- Suggest areas worth exploring further
|
||||
- Help identify patterns in thoughts and behaviors
|
||||
- Encourage gratitude and positive reflection
|
||||
|
||||
**3. Memory Enhancement**
|
||||
|
||||
- Help recall specific details from the day
|
||||
- Connect current events to past experiences
|
||||
- Highlight personal growth and progress
|
||||
- Preserve important conversations or interactions
|
||||
|
||||
**4. Organization**
|
||||
|
||||
- Suggest tags or themes for entries
|
||||
- Create summaries for weekly/monthly reviews
|
||||
- Track recurring topics or goals
|
||||
- Maintain consistency in formatting
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Privacy First**: Treat all content as deeply personal and confidential
|
||||
- **User's Voice**: Write in a way that sounds like the user, not generic
|
||||
- **No Judgment**: Accept all emotions and experiences without criticism
|
||||
- **Encourage Honesty**: Create a safe space for authentic expression
|
||||
- **Balance**: Mix facts with feelings, events with reflections
|
||||
|
||||
## Output Format
|
||||
|
||||
When creating a diary entry, include:
|
||||
|
||||
1. **Date & Title** (optional creative title)
|
||||
2. **Main Content** - The narrative or bullet points
|
||||
3. **Reflection** - A brief closing thought or takeaway
|
||||
4. **Tags** (optional) - For organization and future reference
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "agent-example",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "commands-example",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "custom-commands",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "excludeTools",
|
||||
"name": "exclude-tools-example",
|
||||
"version": "1.0.0",
|
||||
"excludeTools": ["run_shell_command(rm -rf)"]
|
||||
"excludeTools": ["Shell(rm -rf)"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "mcp-server-example",
|
||||
"version": "1.0.0",
|
||||
"description": "Example MCP Server for Gemini CLI Extension",
|
||||
"description": "Example MCP Server for Qwen Code Extension",
|
||||
"type": "module",
|
||||
"main": "example.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "skills-example",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
name: synonyms
|
||||
description: Generate synonyms for words or phrases. Use this skill when the user needs alternative words with similar meanings, wants to expand vocabulary, or seeks varied expressions for writing.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
This skill helps generate synonyms and alternative expressions for given words or phrases. It provides contextually appropriate alternatives to enhance vocabulary and improve writing variety.
|
||||
|
||||
The user provides a word, phrase, or sentence where they need synonym suggestions. They may specify the context, tone, or formality level desired.
|
||||
|
||||
## Synonym Generation Guidelines
|
||||
|
||||
When generating synonyms, consider:
|
||||
|
||||
- **Context**: The specific domain or situation where the word will be used
|
||||
- **Tone**: Formal, informal, neutral, academic, conversational, etc.
|
||||
- **Nuance**: Subtle differences in meaning between similar words
|
||||
- **Register**: Appropriate level of formality for the intended audience
|
||||
|
||||
## Output Format
|
||||
|
||||
For each input word or phrase, provide:
|
||||
|
||||
1. **Direct Synonyms**: Words with nearly identical meanings
|
||||
2. **Related Alternatives**: Words with similar but slightly different connotations
|
||||
3. **Context Examples**: Brief usage examples when helpful
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Prioritize commonly used synonyms over obscure alternatives
|
||||
- Note any subtle differences in meaning or usage
|
||||
- Consider regional variations when relevant
|
||||
- Indicate formality levels (formal/informal/neutral)
|
||||
- Provide multiple options to give users choices
|
||||
|
||||
## Example
|
||||
|
||||
**Input**: "happy"
|
||||
|
||||
**Synonyms**:
|
||||
|
||||
- **Direct**: joyful, cheerful, delighted, pleased, content
|
||||
- **Informal**: thrilled, stoked, over the moon
|
||||
- **Formal**: elated, gratified, blissful
|
||||
- **Subtle variations**:
|
||||
- _content_ - peaceful satisfaction
|
||||
- _ecstatic_ - intense, overwhelming happiness
|
||||
- _cheerful_ - outwardly expressing happiness
|
||||
345
packages/cli/src/commands/extensions/settings.test.ts
Normal file
345
packages/cli/src/commands/extensions/settings.test.ts
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
vi,
|
||||
beforeEach,
|
||||
type MockInstance,
|
||||
} from 'vitest';
|
||||
import { settingsCommand } from './settings.js';
|
||||
import yargs from 'yargs';
|
||||
|
||||
const mockGetLoadedExtensions = vi.hoisted(() => vi.fn());
|
||||
const mockGetScopedEnvContents = vi.hoisted(() => vi.fn());
|
||||
const mockUpdateSetting = vi.hoisted(() => vi.fn());
|
||||
const mockPromptForSetting = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('./utils.js', () => ({
|
||||
getExtensionManager: vi.fn().mockResolvedValue({
|
||||
getLoadedExtensions: mockGetLoadedExtensions,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@qwen-code/qwen-code-core', () => ({
|
||||
ExtensionSettingScope: {
|
||||
USER: 'user',
|
||||
WORKSPACE: 'workspace',
|
||||
},
|
||||
getScopedEnvContents: mockGetScopedEnvContents,
|
||||
promptForSetting: mockPromptForSetting,
|
||||
updateSetting: mockUpdateSetting,
|
||||
}));
|
||||
|
||||
describe('extensions settings command', () => {
|
||||
it('should fail if no subcommand is provided', () => {
|
||||
const validationParser = yargs([])
|
||||
.command(settingsCommand)
|
||||
.fail(false)
|
||||
.locale('en');
|
||||
expect(() => validationParser.parse('settings')).toThrow(
|
||||
'Not enough non-option arguments: got 0, need at least 1',
|
||||
);
|
||||
});
|
||||
|
||||
it('should register set subcommand', () => {
|
||||
const parser = yargs([]).command(settingsCommand).fail(false).locale('en');
|
||||
expect(() => parser.parse('settings set')).toThrow(
|
||||
'Not enough non-option arguments',
|
||||
);
|
||||
});
|
||||
|
||||
it('should register list subcommand', () => {
|
||||
const parser = yargs([]).command(settingsCommand).fail(false).locale('en');
|
||||
expect(() => parser.parse('settings list')).toThrow(
|
||||
'Not enough non-option arguments',
|
||||
);
|
||||
});
|
||||
|
||||
it('should accept set command with name and setting', () => {
|
||||
const parser = yargs([]).command(settingsCommand).fail(false).locale('en');
|
||||
expect(() =>
|
||||
parser.parse('settings set my-extension API_KEY'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should accept set command with scope option', () => {
|
||||
const parser = yargs([]).command(settingsCommand).fail(false).locale('en');
|
||||
expect(() =>
|
||||
parser.parse('settings set my-extension API_KEY --scope=workspace'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should fail set command with invalid scope', () => {
|
||||
const parser = yargs([]).command(settingsCommand).fail(false).locale('en');
|
||||
expect(() =>
|
||||
parser.parse('settings set my-extension API_KEY --scope=invalid'),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('should accept list command with name', () => {
|
||||
const parser = yargs([]).command(settingsCommand).fail(false).locale('en');
|
||||
expect(() => parser.parse('settings list my-extension')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings set handler', () => {
|
||||
let consoleLogSpy: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return early if extension manager is not available', async () => {
|
||||
const { getExtensionManager } = await import('./utils.js');
|
||||
vi.mocked(getExtensionManager).mockResolvedValueOnce(null as never);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings set my-extension API_KEY');
|
||||
|
||||
expect(mockUpdateSetting).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return early if no extensions are loaded', async () => {
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings set my-extension API_KEY');
|
||||
|
||||
expect(mockUpdateSetting).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log error if extension is not found', async () => {
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([
|
||||
{ name: 'other-extension', id: 'other-id', config: {} },
|
||||
]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings set my-extension API_KEY');
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
'Extension "my-extension" not found.',
|
||||
);
|
||||
expect(mockUpdateSetting).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call updateSetting with correct arguments for user scope', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension', settings: [] },
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings set my-extension API_KEY');
|
||||
|
||||
expect(mockUpdateSetting).toHaveBeenCalledWith(
|
||||
mockExtension.config,
|
||||
mockExtension.id,
|
||||
'API_KEY',
|
||||
mockPromptForSetting,
|
||||
'user',
|
||||
);
|
||||
});
|
||||
|
||||
it('should call updateSetting with workspace scope when specified', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension', settings: [] },
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync(
|
||||
'settings set my-extension API_KEY --scope=workspace',
|
||||
);
|
||||
|
||||
expect(mockUpdateSetting).toHaveBeenCalledWith(
|
||||
mockExtension.config,
|
||||
mockExtension.id,
|
||||
'API_KEY',
|
||||
mockPromptForSetting,
|
||||
'workspace',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('settings list handler', () => {
|
||||
let consoleLogSpy: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return early if extension manager is not available', async () => {
|
||||
const { getExtensionManager } = await import('./utils.js');
|
||||
vi.mocked(getExtensionManager).mockResolvedValueOnce(null as never);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(mockGetScopedEnvContents).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return early if no extensions are loaded', async () => {
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(mockGetScopedEnvContents).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log error if extension is not found', async () => {
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([
|
||||
{ name: 'other-extension', id: 'other-id', config: {} },
|
||||
]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
'Extension "my-extension" not found.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should log message if extension has no settings', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension' },
|
||||
settings: [],
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
'Extension "my-extension" has no settings to configure.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should list settings with their values', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension' },
|
||||
settings: [
|
||||
{
|
||||
name: 'API Key',
|
||||
envVar: 'API_KEY',
|
||||
description: 'Your API key',
|
||||
sensitive: false,
|
||||
},
|
||||
{
|
||||
name: 'Secret Token',
|
||||
envVar: 'SECRET_TOKEN',
|
||||
description: 'A secret token',
|
||||
sensitive: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
mockGetScopedEnvContents
|
||||
.mockResolvedValueOnce({ API_KEY: 'my-api-key' }) // user scope
|
||||
.mockResolvedValueOnce({}); // workspace scope
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith('Settings for "my-extension":');
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith('\n- API Key (API_KEY)');
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(' Description: Your API key');
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(' Value: my-api-key (user)');
|
||||
});
|
||||
|
||||
it('should show workspace scope for workspace-scoped settings', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension' },
|
||||
settings: [
|
||||
{
|
||||
name: 'API Key',
|
||||
envVar: 'API_KEY',
|
||||
description: 'Your API key',
|
||||
sensitive: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
mockGetScopedEnvContents
|
||||
.mockResolvedValueOnce({ API_KEY: 'user-value' }) // user scope
|
||||
.mockResolvedValueOnce({ API_KEY: 'workspace-value' }); // workspace scope
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
// Workspace should override user, and show (workspace) scope
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
' Value: workspace-value (workspace)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should show [not set] for undefined settings', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension' },
|
||||
settings: [
|
||||
{
|
||||
name: 'API Key',
|
||||
envVar: 'API_KEY',
|
||||
description: 'Your API key',
|
||||
sensitive: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
mockGetScopedEnvContents
|
||||
.mockResolvedValueOnce({}) // user scope
|
||||
.mockResolvedValueOnce({}); // workspace scope
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(' Value: [not set]');
|
||||
});
|
||||
|
||||
it('should show [value stored in keychain] for sensitive settings', async () => {
|
||||
const mockExtension = {
|
||||
name: 'my-extension',
|
||||
id: 'ext-id-123',
|
||||
config: { name: 'my-extension' },
|
||||
settings: [
|
||||
{
|
||||
name: 'Secret Token',
|
||||
envVar: 'SECRET_TOKEN',
|
||||
description: 'A secret token',
|
||||
sensitive: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockGetLoadedExtensions.mockReturnValueOnce([mockExtension]);
|
||||
mockGetScopedEnvContents
|
||||
.mockResolvedValueOnce({ SECRET_TOKEN: 'secret-value' }) // user scope
|
||||
.mockResolvedValueOnce({}); // workspace scope
|
||||
|
||||
const parser = yargs([]).command(settingsCommand);
|
||||
await parser.parseAsync('settings list my-extension');
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
' Value: [value stored in keychain] (user)',
|
||||
);
|
||||
});
|
||||
});
|
||||
148
packages/cli/src/commands/extensions/settings.ts
Normal file
148
packages/cli/src/commands/extensions/settings.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { CommandModule } from 'yargs';
|
||||
import { getExtensionManager } from './utils.js';
|
||||
import {
|
||||
ExtensionSettingScope,
|
||||
getScopedEnvContents,
|
||||
promptForSetting,
|
||||
updateSetting,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
|
||||
// --- SET COMMAND ---
|
||||
interface SetArgs {
|
||||
name: string;
|
||||
setting: string;
|
||||
scope: string;
|
||||
}
|
||||
|
||||
const setCommand: CommandModule<object, SetArgs> = {
|
||||
command: 'set [--scope] <name> <setting>',
|
||||
describe: 'Set a specific setting for an extension.',
|
||||
builder: (yargs) =>
|
||||
yargs
|
||||
.positional('name', {
|
||||
describe: 'Name of the extension to configure.',
|
||||
type: 'string',
|
||||
demandOption: true,
|
||||
})
|
||||
.positional('setting', {
|
||||
describe: 'The setting to configure (name or env var).',
|
||||
type: 'string',
|
||||
demandOption: true,
|
||||
})
|
||||
.option('scope', {
|
||||
describe: 'The scope to set the setting in.',
|
||||
type: 'string',
|
||||
choices: ['user', 'workspace'],
|
||||
default: 'user',
|
||||
}),
|
||||
handler: async (args) => {
|
||||
const { name, setting, scope } = args;
|
||||
const extensionManager = await getExtensionManager();
|
||||
if (!extensionManager) return;
|
||||
const extensions = extensionManager.getLoadedExtensions();
|
||||
if (!extensions || extensions.length === 0) return;
|
||||
const extension = extensions.find((e) => e.name === name);
|
||||
if (!extension) {
|
||||
console.log(`Extension "${name}" not found.`);
|
||||
return;
|
||||
}
|
||||
await updateSetting(
|
||||
extension.config,
|
||||
extension.id,
|
||||
setting,
|
||||
promptForSetting,
|
||||
scope as ExtensionSettingScope,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
// --- LIST COMMAND ---
|
||||
interface ListArgs {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const listCommand: CommandModule<object, ListArgs> = {
|
||||
command: 'list <name>',
|
||||
describe: 'List all settings for an extension.',
|
||||
builder: (yargs) =>
|
||||
yargs.positional('name', {
|
||||
describe: 'Name of the extension.',
|
||||
type: 'string',
|
||||
demandOption: true,
|
||||
}),
|
||||
handler: async (args) => {
|
||||
const { name } = args;
|
||||
const extensionManager = await getExtensionManager();
|
||||
if (!extensionManager) return;
|
||||
const extensions = extensionManager.getLoadedExtensions();
|
||||
if (!extensions || extensions.length === 0) return;
|
||||
const extension = extensions.find((e) => e.name === name);
|
||||
if (!extension) {
|
||||
console.log(`Extension "${name}" not found.`);
|
||||
return;
|
||||
}
|
||||
if (!extension || !extension.settings || extension.settings.length === 0) {
|
||||
console.log(`Extension "${name}" has no settings to configure.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const userSettings = await getScopedEnvContents(
|
||||
extension.config,
|
||||
extension.id,
|
||||
ExtensionSettingScope.USER,
|
||||
);
|
||||
const workspaceSettings = await getScopedEnvContents(
|
||||
extension.config,
|
||||
extension.id,
|
||||
ExtensionSettingScope.WORKSPACE,
|
||||
);
|
||||
const mergedSettings = { ...userSettings, ...workspaceSettings };
|
||||
|
||||
console.log(`Settings for "${name}":`);
|
||||
for (const setting of extension.settings) {
|
||||
const value = mergedSettings[setting.envVar];
|
||||
let displayValue: string;
|
||||
let scopeInfo = '';
|
||||
|
||||
if (workspaceSettings[setting.envVar] !== undefined) {
|
||||
scopeInfo = ' (workspace)';
|
||||
} else if (userSettings[setting.envVar] !== undefined) {
|
||||
scopeInfo = ' (user)';
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
displayValue = '[not set]';
|
||||
} else if (setting.sensitive) {
|
||||
displayValue = '[value stored in keychain]';
|
||||
} else {
|
||||
displayValue = value;
|
||||
}
|
||||
console.log(`
|
||||
- ${setting.name} (${setting.envVar})`);
|
||||
console.log(` Description: ${setting.description}`);
|
||||
console.log(` Value: ${displayValue}${scopeInfo}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// --- SETTINGS COMMAND ---
|
||||
export const settingsCommand: CommandModule = {
|
||||
command: 'settings <command>',
|
||||
describe: 'Manage extension settings.',
|
||||
builder: (yargs) =>
|
||||
yargs
|
||||
.command(setCommand)
|
||||
.command(listCommand)
|
||||
.demandCommand(1, 'You need to specify a command (set or list).')
|
||||
.version(false),
|
||||
handler: () => {
|
||||
// This handler is not called when a subcommand is provided.
|
||||
// Yargs will show the help menu.
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue