mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat: Enhance A2UI with RadioGroup and Markdown support
- Added support for RadioGroup component in A2UI, allowing single selection from multiple options. - Implemented Markdown parsing in A2UIPopupCard for better content rendering. - Updated A2UIPopupCard to handle different question types and improved layout for multi-select and single-select questions. - Introduced new utility functions for handling disabled items during installation. - Enhanced installation process to restore previously disabled skills and commands. - Updated notification store and related tests to accommodate new features. - Adjusted Vite configuration for better development experience.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, rmdirSync, appendFileSync } from 'fs';
|
||||
import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, rmdirSync, appendFileSync, renameSync } from 'fs';
|
||||
import { join, dirname, basename } from 'path';
|
||||
import { homedir, platform } from 'os';
|
||||
import { fileURLToPath } from 'url';
|
||||
@@ -45,6 +45,145 @@ interface CopyResult {
|
||||
directories: number;
|
||||
}
|
||||
|
||||
// Disabled item tracking for install process
|
||||
interface DisabledItem {
|
||||
name: string;
|
||||
path: string;
|
||||
type: 'skill' | 'command';
|
||||
}
|
||||
|
||||
interface DisabledItems {
|
||||
skills: DisabledItem[];
|
||||
commands: DisabledItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for disabled skills and commands before installation
|
||||
* Skills: look for SKILL.md.disabled files
|
||||
* Commands: look for *.md.disabled files
|
||||
*/
|
||||
function scanDisabledItems(installPath: string, globalPath?: string): DisabledItems {
|
||||
const result: DisabledItems = { skills: [], commands: [] };
|
||||
const pathsToScan = [installPath];
|
||||
if (globalPath && globalPath !== installPath) {
|
||||
pathsToScan.push(globalPath);
|
||||
}
|
||||
|
||||
for (const basePath of pathsToScan) {
|
||||
// Scan skills
|
||||
const skillsDir = join(basePath, '.claude', 'skills');
|
||||
if (existsSync(skillsDir)) {
|
||||
try {
|
||||
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const disabledPath = join(skillsDir, entry.name, 'SKILL.md.disabled');
|
||||
if (existsSync(disabledPath)) {
|
||||
result.skills.push({
|
||||
name: entry.name,
|
||||
path: disabledPath,
|
||||
type: 'skill'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
// Scan commands recursively
|
||||
const commandsDir = join(basePath, '.claude', 'commands');
|
||||
if (existsSync(commandsDir)) {
|
||||
scanDisabledCommandsRecursive(commandsDir, commandsDir, result.commands);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scan for disabled command files
|
||||
*/
|
||||
function scanDisabledCommandsRecursive(baseDir: string, currentDir: string, results: DisabledItem[]): void {
|
||||
try {
|
||||
const entries = readdirSync(currentDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(currentDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
scanDisabledCommandsRecursive(baseDir, fullPath, results);
|
||||
} else if (entry.isFile() && entry.name.endsWith('.md.disabled')) {
|
||||
const relativePath = fullPath.substring(baseDir.length + 1);
|
||||
const commandName = relativePath.replace(/\.disabled$/, '');
|
||||
results.push({
|
||||
name: commandName,
|
||||
path: fullPath,
|
||||
type: 'command'
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore disabled state after installation
|
||||
* For each previously disabled item, if the enabled version exists, rename it back to disabled
|
||||
*/
|
||||
function restoreDisabledState(
|
||||
disabledItems: DisabledItems,
|
||||
installPath: string,
|
||||
globalPath?: string
|
||||
): { skillsRestored: number; commandsRestored: number } {
|
||||
let skillsRestored = 0;
|
||||
let commandsRestored = 0;
|
||||
|
||||
// Restore skills
|
||||
for (const skill of disabledItems.skills) {
|
||||
// Determine which path this skill belongs to
|
||||
const skillDir = dirname(skill.path);
|
||||
const enabledPath = join(skillDir, 'SKILL.md');
|
||||
const disabledPath = join(skillDir, 'SKILL.md.disabled');
|
||||
|
||||
// If enabled version was installed, rename it to disabled
|
||||
if (existsSync(enabledPath)) {
|
||||
try {
|
||||
// Remove old disabled file if it still exists (shouldn't, but be safe)
|
||||
if (existsSync(disabledPath)) {
|
||||
unlinkSync(disabledPath);
|
||||
}
|
||||
renameSync(enabledPath, disabledPath);
|
||||
skillsRestored++;
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore commands
|
||||
for (const command of disabledItems.commands) {
|
||||
const enabledPath = command.path.replace(/\.disabled$/, '');
|
||||
const disabledPath = command.path;
|
||||
|
||||
// If enabled version was installed, rename it to disabled
|
||||
if (existsSync(enabledPath)) {
|
||||
try {
|
||||
// Remove old disabled file if it still exists
|
||||
if (existsSync(disabledPath)) {
|
||||
unlinkSync(disabledPath);
|
||||
}
|
||||
renameSync(enabledPath, disabledPath);
|
||||
commandsRestored++;
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { skillsRestored, commandsRestored };
|
||||
}
|
||||
|
||||
// Get package root directory (ccw/src/commands -> ccw)
|
||||
function getPackageRoot(): string {
|
||||
return join(__dirname, '..', '..');
|
||||
@@ -204,6 +343,14 @@ export async function installCommand(options: InstallOptions): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for disabled items before installation
|
||||
const globalPath = mode === 'Path' ? homedir() : undefined;
|
||||
const disabledItems = scanDisabledItems(installPath, globalPath);
|
||||
const totalDisabled = disabledItems.skills.length + disabledItems.commands.length;
|
||||
if (totalDisabled > 0) {
|
||||
info(`Found ${totalDisabled} disabled items (${disabledItems.skills.length} skills, ${disabledItems.commands.length} commands)`);
|
||||
}
|
||||
|
||||
// Create manifest
|
||||
const manifest = createManifest(mode, installPath);
|
||||
|
||||
@@ -213,6 +360,7 @@ export async function installCommand(options: InstallOptions): Promise<void> {
|
||||
|
||||
let totalFiles = 0;
|
||||
let totalDirs = 0;
|
||||
let restoreStats = { skillsRestored: 0, commandsRestored: 0 };
|
||||
|
||||
try {
|
||||
// For Path mode, install workflows to global first
|
||||
@@ -259,6 +407,15 @@ export async function installCommand(options: InstallOptions): Promise<void> {
|
||||
|
||||
spinner.succeed('Installation complete!');
|
||||
|
||||
// Restore disabled state for previously disabled items
|
||||
if (totalDisabled > 0) {
|
||||
restoreStats = restoreDisabledState(disabledItems, installPath, globalPath);
|
||||
const totalRestored = restoreStats.skillsRestored + restoreStats.commandsRestored;
|
||||
if (totalRestored > 0) {
|
||||
info(`Restored ${totalRestored} disabled items (${restoreStats.skillsRestored} skills, ${restoreStats.commandsRestored} commands)`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
spinner.fail('Installation failed');
|
||||
const errMsg = err as Error;
|
||||
@@ -290,6 +447,12 @@ export async function installCommand(options: InstallOptions): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add restore stats if any disabled items were restored
|
||||
if (restoreStats.skillsRestored > 0 || restoreStats.commandsRestored > 0) {
|
||||
const totalRestored = restoreStats.skillsRestored + restoreStats.commandsRestored;
|
||||
summaryLines.push(chalk.gray(`Disabled state restored: ${totalRestored} items`));
|
||||
}
|
||||
|
||||
summaryLines.push('');
|
||||
summaryLines.push(chalk.gray(`Manifest: ${basename(manifestPath)}`));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user