Skip to content

Feature/migrate keys#92

Merged
divyeshio merged 4 commits intomainfrom
feature/migrate-keys
Mar 4, 2026
Merged

Feature/migrate keys#92
divyeshio merged 4 commits intomainfrom
feature/migrate-keys

Conversation

@divyeshio
Copy link
Owner

No description provided.

- Updated CommandDialog to use command keys for updates.
- Refactored ExclusionGroupsDialog to replace command IDs with keys in various functions.
- Modified ParameterDetailsDialog to handle parameters using keys instead of IDs.
- Adjusted SavedCommandsDialog to delete commands using keys.
- Changed HelpMenu to filter commands and parameters based on keys.
- Updated ParameterList to utilize keys for parameter management.
- Refactored tool-editor store actions to work with command and parameter keys.
- Adjusted AIParsing component to replace IDs with keys during tool parsing.
- Updated saved commands handling in routes to use keys instead of IDs.
- Removed UUID dependency where applicable and replaced with slugified keys.
- Added a new `Explore` agent for efficient codebase exploration and Q&A.
- Refactored tool schemas to replace `supportedInput` and `supportedOutput` with a unified `metadata` object.
- Created a new nested JSON schema for tools, enhancing the structure and validation.
- Updated existing tools to utilize the new metadata format, ensuring consistency across the codebase.
- Modified related utility functions and tests to accommodate the new metadata structure.
Copilot AI review requested due to automatic review settings March 4, 2026 05:47
…luding key generation and import adjustments
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the Commandly tool/editor data model away from UUID-based id fields to string-based key fields, and consolidates tool I/O capabilities under a metadata object while updating generated specs and bundled tool JSON to match.

Changes:

  • Replace id/*Id fields with key/*Key across Commandly types, editor/store logic, runtime preview, and routes.
  • Move supportedInput/supportedOutput into tool.metadata and update tool cards, scripts, MCP output, and public JSON assets.
  • Generate both flat and nested JSON schema specifications and add nested specification output.

Reviewed changes

Copilot reviewed 43 out of 44 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/routes/tools/index.tsx Stops generating/storing tool UUID ids during navigation/import.
src/routes/tools/$toolName/index.tsx Updates saved command + command list handling to use key and tool name as id basis.
src/lib/utils.ts Replaces replaceId with replaceKey migration helper using slug-based keys.
src/lib/types.ts Adds schemas/types for supported tool I/O metadata.
src/components/tool-editor-ui/tool-card.tsx Reads supported I/O from tool.metadata instead of top-level fields.
src/components/tool-editor-ui/dialogs/saved-commands-dialog.tsx Switches SavedCommand list keys/deletes from id to key.
src/components/tool-editor-ui/ai-parsing.tsx Uses replaceKey instead of replaceId after schema validation.
scripts/generate-tools-json.ts Emits metadata instead of top-level supported I/O arrays.
scripts/generate-specification.ts Writes both public/specification/flat.json and nested.json.
registry/commandly/tool-editor/tool-editor.tsx Updates saved command delete/load paths to use tool name + key.
registry/commandly/tool-editor/tool-editor.store.ts Migrates selectors/actions from ids to keys; updates saved command / exclusion group handling.
registry/commandly/tool-editor/parameter-list.tsx Uses commandKey / parameter.key / group.key throughout list rendering/actions.
registry/commandly/tool-editor/help-menu.tsx Migrates command hierarchy traversal from parent ids to parent keys.
registry/commandly/tool-editor/dialogs/tool-details-dialog.tsx Writes supported I/O into tool.metadata and imports types from src/lib/types.
registry/commandly/tool-editor/dialogs/saved-commands-dialog.tsx Migrates props and UI list keys/deletes from id to key.
registry/commandly/tool-editor/dialogs/parameter-details-dialog.tsx Migrates dependency/validation/enum value identifiers to key and updates reference fields to *Key.
registry/commandly/tool-editor/dialogs/exclusion-groups-dialog.tsx Migrates exclusion group editing to key and parameterKeys.
registry/commandly/tool-editor/dialogs/command-dialog.tsx Updates save call to updateCommand to use command key.
registry/commandly/tool-editor/command-tree.tsx Migrates tree expansion/selection/delete/add flows from ids to keys.
registry/commandly/runtime-preview.tsx Uses parameter.key and commandKey for rendering and value updates.
registry/commandly/lib/utils/commandly.ts Adds slugify, migrates command/parameter creation and hierarchy helpers to keys, moves supported I/O to metadata.
registry/commandly/lib/utils/commandly-nested.ts Updates nested conversion to resolve dependencies/relationships via keys and moves supported I/O to metadata.
registry/commandly/lib/types/commandly.ts Updates Zod schemas/types from ids to keys; replaces supported I/O schemas with generic metadata.
registry/commandly/lib/types/commandly-nested.ts Replaces TS interfaces with Zod schemas and adds nested tool metadata schema.
registry/commandly/generated-command.tsx Uses commandKey and parameter keys for value lookup/filtering.
registry/commandly/tests/tool-editor/parameter-list.test.tsx Updates test fixtures/assertions for key-based model.
registry/commandly/tests/tool-editor/dialogs/parameter-details-dialog.test.tsx Updates test fixtures/assertions for key-based model.
registry/commandly/tests/tool-editor/dialogs/command-dialog.test.tsx Updates test fixtures/assertions for key-based command identity.
registry/commandly/tests/tool-editor/command-tree.test.tsx Updates command tree tests for parentCommandKey/key model + metadata presence.
registry/commandly/tests/generated-command.test.tsx Updates mocked store/tool data to include keys.
public/tools.json Updates tool list entries to use metadata for supported I/O.
public/tools-collection/yt-dlp.json Migrates tool JSON to metadata + key-based commands/parameters.
public/tools-collection/urlfinder.json Migrates tool JSON to metadata + key-based commands/parameters.
public/tools-collection/subfinder.json Migrates tool JSON to metadata + key-based commands/parameters.
public/tools-collection/shuffledns.json Migrates tool JSON to metadata + key-based commands/parameters.
public/tools-collection/dnsx.json Migrates tool JSON to metadata + key-based commands/parameters.
public/tools-collection/cdncheck.json Migrates tool JSON to metadata + key-based commands/parameters/exclusion groups.
public/tools-collection/asnmap.json Migrates tool JSON to metadata + key-based commands/parameters.
public/specification/nested.json Adds generated nested JSON schema spec output.
public/specification/flat.json Updates generated flat JSON schema spec for key-based model + metadata.
mcp/index.ts Emits metadata instead of supportedInput/supportedOutput fields.
.github/agents/Explore.agent.md Adds an exploration subagent configuration for repo Q&A/search.
Comments suppressed due to low confidence (3)

registry/commandly/tool-editor/dialogs/tool-details-dialog.tsx:109

  • defaultValue={tool.metadata.supportedInput} (and supportedOutput below) assumes those properties always exist. But ToolSchema currently types metadata as a generic record, so these may be undefined at runtime (e.g., older tools), which can break MultiSelect if it expects an array. Provide a safe fallback (e.g., tool.metadata.supportedInput ?? []) or validate metadata against a schema that requires these arrays.
    registry/commandly/tool-editor/tool-editor.store.ts:194
  • Saved command keys are generated via slugify(command.substring(0, 20)), which can collide for different commands with the same prefix. Since deletion/removal uses the key as the identifier, collisions can cause the wrong entry to be deleted or make entries unreachable. Use a collision-resistant key (e.g., uuid, hash of full command, or slug + unique suffix) and keep it stable.
    registry/commandly/lib/utils/commandly.ts:304
  • createNewParameter initializes key as an empty string. Because the rest of the editor now treats key as the primary identifier (React keys, updates, deletes, upserts), creating multiple new parameters or saving before a key is generated can cause duplicate identifiers and incorrect upsert/delete behavior. Consider generating a temporary unique key at creation time (and keeping it stable) or enforce key assignment before the parameter can be saved/used.
export const createNewParameter = (isGlobal: boolean, commandKey?: string): Parameter => {
  return {
    key: "",
    name: "",
    commandKey: isGlobal ? undefined : commandKey,
    description: "",
    parameterType: "Option",
    dataType: "String",
    isRequired: false,

Comment on lines 91 to 92
addSavedCommandToStorage(`saved-${toolId}`, newSavedCommand);
setSavedCommands(getSavedCommandsFromStorage(toolId));
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addSavedCommandToStorage is being called with a toolId that already includes the saved- prefix (addSavedCommandToStorage(saved-${toolId}, ...)). Since the storage helpers themselves prefix with saved- when reading, this causes reads from saved-saved-${toolId} and will overwrite existing saved commands instead of appending. Pass the un-prefixed tool id consistently (e.g., tool.name) and keep the prefixing logic in one place (either helpers or callers, not both).

Copilot uses AI. Check for mistakes.
Comment on lines 100 to 103
if (!tool) return;
const toolId = tool.id || tool.name;
removeSavedCommandFromStorage(`saved-${toolId}`, commandId);
const toolId = tool.name;
removeSavedCommandFromStorage(`saved-${toolId}`, commandKey);
setSavedCommands(getSavedCommandsFromStorage(toolId));
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeSavedCommandFromStorage is called with a toolId that includes the saved- prefix. With the current helper implementations this results in reading from saved-saved-... and can clear or fail to remove the intended entry. Standardize the storage key format (prefer passing the un-prefixed tool id to all helpers) to avoid double-prefixing.

Copilot uses AI. Check for mistakes.
Comment on lines 193 to 205
addSavedCommandToStorage(`saved-${toolId}`, newSavedCommand);

toast("Command Saved", {
description: "Command has been saved successfully.",
});
},

removeSavedCommand(commandId: string) {
removeSavedCommand(commandKey: string) {
toolBuilderStore.setState((state) => {
const toolId = state.tool.id || state.tool.name;
removeSavedCommandFromStorage(`saved-${toolId}`, commandId);
const toolId = state.tool.name;
removeSavedCommandFromStorage(`saved-${toolId}`, commandKey);
return { ...state };
});
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addSavedCommandToStorage / removeSavedCommandFromStorage are being called with a toolId that includes the saved- prefix. The underlying helpers already apply the prefix when reading, so this double-prefixing (saved-saved-...) breaks append/remove semantics and can overwrite or clear the list unexpectedly. Pass the un-prefixed id consistently and ensure helpers use the same key for get/set/remove.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +37
const handleDeleteCommand = (commandKey: string) => {
const toolId = tool.name;
removeSavedCommandFromStorage(`saved-${toolId}`, commandKey);
setSavedCommands(savedCommands.filter((cmd) => cmd.key !== commandKey));
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeSavedCommandFromStorage is invoked with a toolId that already includes the saved- prefix. Given the current helper implementations, this can lead to reads from saved-saved-... and cause removals to fail or clear the wrong entry list. Standardize storage key construction in one place and pass a consistent (un-prefixed) tool id.

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +116
setParameter((prev) => {
const next = { ...prev, ...updates };
if (updates.name || updates.longFlag) {
let generatedKey = slugify(next.longFlag || next.name);

// Get current tool state to check for duplicate keys
const toolState = toolBuilderStore.state;
const existingParam = toolState.tool.parameters.find(
(p) => p.key === generatedKey && p.key !== prev.key,
);

// If duplicate exists and parameter is not global, prefix with command key
if (existingParam && !next.isGlobal && commandKey) {
generatedKey = `${commandKey}-${generatedKey}`;
}

next.key = generatedKey;
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateParameter auto-regenerates parameter.key whenever name/longFlag changes. Since key is now the primary identifier (and upsertParameter matches by key), changing it will typically create a new parameter on save and leave the old one behind, and it can also break references (exclusionGroups.parameterKeys, dependency parameterKey/dependsOnParameterKey, parameterValues keyed by parameter.key). Keys should be stable once created, or key changes must be migrated across the entire tool state in one transaction.

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +36
const keyMap: Record<string, string> = {};

// Map tool id
if (clone.id) {
idMap[clone.id] = uuidv7();
function mapKey(oldKey: string, newKey: string): string {
if (!oldKey) return "";
keyMap[oldKey] = newKey;
return keyMap[oldKey];
}

// Map command ids
clone.commands.forEach((cmd) => {
idMap[cmd.id] = uuidv7();
if (cmd.parentCommandId) idMap[cmd.parentCommandId] = uuidv7();
// Map command keys (handle commandId as well during migration)
clone.commands.forEach((cmd: Command) => {
const oldKey = cmd.key;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaceKey only records a mapping when oldKey is truthy (if (!oldKey) return ""). If a parsed tool contains empty-string keys (allowed by the current schemas and also produced by createNewParameter), the "replacement" step will keep empty keys, which can lead to duplicate identifiers and broken lookups. Consider generating keys even when the existing key is empty/missing, and ensure generated keys are unique within the tool.

Copilot uses AI. Check for mistakes.
Comment on lines +342 to 346
export const removeSavedCommandFromStorage = (toolId: string, commandKey: string): void => {
const existingCommands = getSavedCommandsFromStorage(toolId);
const updatedCommands = existingCommands.filter((cmd) => cmd.id !== commandId);
const updatedCommands = existingCommands.filter((cmd) => cmd.key !== commandKey);
saveSavedCommandsToStorage(toolId, updatedCommands);
};
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The saved-command storage helpers are inconsistent about whether toolId already includes the saved- prefix: getSavedCommandsFromStorage reads from saved-${toolId}, while saveSavedCommandsToStorage writes to toolId directly. When callers pass an already-prefixed id (e.g., saved-${tool.name}), reads happen from saved-saved-..., which makes removeSavedCommandFromStorage behave like a full reset and makes addSavedCommandToStorage overwrite instead of append. Unify the contract (either always prefix inside helpers, or always prefix at call sites) and ensure get/set/remove all use the same key.

Copilot uses AI. Check for mistakes.
@divyeshio divyeshio merged commit acc12db into main Mar 4, 2026
6 checks passed
@divyeshio divyeshio deleted the feature/migrate-keys branch March 4, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants