You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@magpie.apache.org by po...@apache.org on 2026/10/16 07:16:19 UTC
(magpie) branch main updated: feat(utilities): add zero-token /magpie-skills Claude Code mod command (part of #1497) (#1667)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/magpie.git
The following commit(s) were added to refs/heads/main by this push:
new 861c84e0 feat(utilities): add zero-token /magpie-skills Claude Code mod command (part of #1497) (#1667)
861c84e0 is described below
commit 861c84e02d6f670f402f82038bf034ec0e5cc6b1
Author: Vardhman Gupta <11...@users.noreply.github.com>
AuthorDate: Fri Oct 16 12:46:13 2026 +0530
feat(utilities): add zero-token /magpie-skills Claude Code mod command (part of #1497) (#1667)
Registers /magpie-skills via Claude Code mod hooks ($.command.register and command.run) in plugins/magpie-utilities to provide instant, zero-token skill discovery.
- Hooks session.start to register /magpie-skills with immediate: true
- Intercepts command.run to execute list_skills.py locally without model turns
- Enforces 30s timeout, UTF-8 output encoding, and python3 -> python fallback
- Handles --verbose, -v, --help, -h, and --root (including paths with spaces and quotes)
- Adds comprehensive unit test suite covering all execution and error handling branches
---
plugins/magpie-utilities/hooks/hooks.json | 7 +
.../magpie-utilities/hooks/skills-command.test.ts | 388 +++++++++++++++++++++
plugins/magpie-utilities/hooks/skills-command.ts | 196 +++++++++++
3 files changed, 591 insertions(+)
diff --git a/plugins/magpie-utilities/hooks/hooks.json b/plugins/magpie-utilities/hooks/hooks.json
new file mode 100644
index 00000000..e1dbbcfc
--- /dev/null
+++ b/plugins/magpie-utilities/hooks/hooks.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://code.claude.com/schemas/hooks.json",
+ "description": "Apache Magpie — Claude Code mods for zero-token skill discovery.",
+ "modules": [
+ "./skills-command.ts"
+ ]
+}
diff --git a/plugins/magpie-utilities/hooks/skills-command.test.ts b/plugins/magpie-utilities/hooks/skills-command.test.ts
new file mode 100644
index 00000000..ae63293b
--- /dev/null
+++ b/plugins/magpie-utilities/hooks/skills-command.test.ts
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import { describe, test } from 'claude-code/testing';
+import {
+ executeMagpieSkills,
+ joinPath,
+ type ProcessRunner,
+ type ProcessRunResult,
+} from './skills-command.ts';
+
+function assert(condition: any, message?: string): void {
+ if (!condition) {
+ throw new Error(message || 'Assertion failed');
+ }
+}
+
+function assertStrictEqual(actual: any, expected: any, message?: string): void {
+ if (actual !== expected) {
+ throw new Error(message || `Expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+ }
+}
+
+function assertDeepStrictEqual(actual: any, expected: any): void {
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
+ throw new Error(`Expected deep equality: ${JSON.stringify(expected)} !== ${JSON.stringify(actual)}`);
+ }
+}
+
+describe('magpie-skills mod command (Pilot 3)', () => {
+ const workspaceDir = '/test/workspace';
+ const scriptPath = '/test/plugin/skills/list-skills/scripts/list_skills.py';
+ const MOCK_SKILLS_OUTPUT =
+ 'Skills installed for this repository (74 total)\n==================================================\n\nsetup/ (8)\n setup Install, update, or verify Apache Magpie';
+ const MOCK_VERBOSE_OUTPUT =
+ 'Skills installed for this repository (74 total)\n==================================================\n\nsetup/ (8)\n setup\n Install, update, or verify Apache Magpie';
+
+ describe('joinPath helper', () => {
+ test('normalizes single and multiple path segments without node:path', () => {
+ assertStrictEqual(joinPath(), '.');
+ assertStrictEqual(joinPath(''), '.');
+ assertStrictEqual(joinPath('a', 'b', 'c'), 'a/b/c');
+ assertStrictEqual(joinPath('a/', '/b/', '/c'), 'a/b/c');
+ assertStrictEqual(joinPath('/root', 'dir', 'file.txt'), '/root/dir/file.txt');
+ assertStrictEqual(joinPath('a', '..', 'b'), 'b');
+ assertStrictEqual(joinPath('a', '.', 'b'), 'a/b');
+ assertStrictEqual(joinPath('a/b/c', '../..'), 'a');
+ });
+ });
+
+ describe('executeMagpieSkills logic', () => {
+ test('invokes python3 with default workspace repo passed to --root', async () => {
+ const calls: Array<{ argv: readonly string[]; init?: any }> = [];
+ const runner: ProcessRunner = async (argv, init) => {
+ calls.push({ argv, init });
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(runner, scriptPath, workspaceDir);
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ workspaceDir,
+ ]);
+ assertStrictEqual(calls[0]?.init?.cwd, workspaceDir);
+ assertStrictEqual(calls[0]?.init?.env?.PYTHONIOENCODING, 'utf-8');
+ assertStrictEqual(calls[0]?.init?.timeoutMs, 30_000);
+ });
+
+ test('honors --verbose flag', async () => {
+ const calls: Array<{ argv: readonly string[] }> = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push({ argv });
+ return { exitCode: 0, stdout: MOCK_VERBOSE_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(runner, scriptPath, workspaceDir, '--verbose');
+ assertStrictEqual(result.text, MOCK_VERBOSE_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ workspaceDir,
+ '--verbose',
+ ]);
+ });
+
+ test('honors -v shorthand flag and case-insensitivity', async () => {
+ const calls: Array<{ argv: readonly string[] }> = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push({ argv });
+ return { exitCode: 0, stdout: MOCK_VERBOSE_OUTPUT, stderr: '' };
+ };
+
+ const result1 = await executeMagpieSkills(runner, scriptPath, workspaceDir, '-v');
+ assertStrictEqual(result1.text, MOCK_VERBOSE_OUTPUT);
+
+ const result2 = await executeMagpieSkills(runner, scriptPath, workspaceDir, '--VERBOSE');
+ assertStrictEqual(result2.text, MOCK_VERBOSE_OUTPUT);
+ assertStrictEqual(calls.length, 2);
+ });
+
+ test('honors --root custom path argument', async () => {
+ const customRoot = '/custom/repo/path';
+ const calls: Array<{ argv: readonly string[]; init?: any }> = [];
+ const runner: ProcessRunner = async (argv, init) => {
+ calls.push({ argv, init });
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(
+ runner,
+ scriptPath,
+ workspaceDir,
+ `--root ${customRoot}`
+ );
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ customRoot,
+ ]);
+ assertStrictEqual(calls[0]?.init?.cwd, workspaceDir);
+ });
+
+ test('honors --root custom path argument with spaces and double quotes', async () => {
+ const customRootWithSpaces = 'C:\\Users\\Jane Doe\\Projects\\repo';
+ const calls: Array<{ argv: readonly string[]; init?: any }> = [];
+ const runner: ProcessRunner = async (argv, init) => {
+ calls.push({ argv, init });
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(
+ runner,
+ scriptPath,
+ workspaceDir,
+ `--root "${customRootWithSpaces}"`
+ );
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ customRootWithSpaces,
+ ]);
+ assertStrictEqual(calls[0]?.init?.cwd, workspaceDir);
+ });
+
+ test('honors --root custom path argument with spaces and single quotes', async () => {
+ const customRootWithSpaces = '/path with spaces/my-repo';
+ const calls: Array<{ argv: readonly string[]; init?: any }> = [];
+ const runner: ProcessRunner = async (argv, init) => {
+ calls.push({ argv, init });
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(
+ runner,
+ scriptPath,
+ workspaceDir,
+ `--root='${customRootWithSpaces}' -v`
+ );
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ customRootWithSpaces,
+ '--verbose',
+ ]);
+ assertStrictEqual(calls[0]?.init?.cwd, workspaceDir);
+ });
+
+ test('honors --root=custom path syntax', async () => {
+ const customRoot = '/custom/another-repo';
+ const calls: Array<{ argv: readonly string[] }> = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push({ argv });
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(
+ runner,
+ scriptPath,
+ workspaceDir,
+ `--root=${customRoot} --verbose`
+ );
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ customRoot,
+ '--verbose',
+ ]);
+ });
+
+ test('falls back to workspaceDir when --root is passed without path and followed by flag', async () => {
+ const calls: Array<{ argv: readonly string[] }> = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push({ argv });
+ return { exitCode: 0, stdout: MOCK_VERBOSE_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(
+ runner,
+ scriptPath,
+ workspaceDir,
+ '--root --verbose'
+ );
+ assertStrictEqual(result.text, MOCK_VERBOSE_OUTPUT);
+ assertStrictEqual(calls.length, 1);
+ assertDeepStrictEqual(calls[0]?.argv, [
+ 'python3',
+ scriptPath,
+ '--root',
+ workspaceDir,
+ '--verbose',
+ ]);
+ });
+
+ test('honors --help and -h flags, passing --help directly to list_skills.py', async () => {
+ const HELP_OUTPUT = 'usage: list_skills.py [-h] [--verbose] [--root ROOT]';
+ const calls: Array<{ argv: readonly string[] }> = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push({ argv });
+ return { exitCode: 0, stdout: HELP_OUTPUT, stderr: '' };
+ };
+
+ const result1 = await executeMagpieSkills(runner, scriptPath, workspaceDir, '--help');
+ assertStrictEqual(result1.text, HELP_OUTPUT);
+ assertDeepStrictEqual(calls[0]?.argv, ['python3', scriptPath, '--help']);
+
+ const result2 = await executeMagpieSkills(runner, scriptPath, workspaceDir, '-h');
+ assertStrictEqual(result2.text, HELP_OUTPUT);
+ assertDeepStrictEqual(calls[1]?.argv, ['python3', scriptPath, '--help']);
+ assertStrictEqual(calls.length, 2);
+ });
+
+ test('falls back cleanly to python when python3 command throws (e.g. ENOENT)', async () => {
+ const calls: string[][] = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push([...argv]);
+ if (argv[0] === 'python3') {
+ throw new Error('spawn python3 ENOENT');
+ }
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(runner, scriptPath, workspaceDir);
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 2);
+ assertStrictEqual(calls[0]?.[0], 'python3');
+ assertStrictEqual(calls[1]?.[0], 'python');
+ assertDeepStrictEqual(calls[1]?.slice(1), [
+ scriptPath,
+ '--root',
+ workspaceDir,
+ ]);
+ });
+
+ test('falls back to python when python3 exits with non-zero exit code', async () => {
+ const calls: string[][] = [];
+ const runner: ProcessRunner = async (argv) => {
+ calls.push([...argv]);
+ if (argv[0] === 'python3') {
+ return { exitCode: 127, stdout: '', stderr: 'python3 not found' };
+ }
+ return { exitCode: 0, stdout: MOCK_SKILLS_OUTPUT, stderr: '' };
+ };
+
+ const result = await executeMagpieSkills(runner, scriptPath, workspaceDir);
+ assertStrictEqual(result.text, MOCK_SKILLS_OUTPUT);
+ assertStrictEqual(calls.length, 2);
+ assertStrictEqual(calls[0]?.[0], 'python3');
+ assertStrictEqual(calls[1]?.[0], 'python');
+ });
+
+ test('returns actionable diagnostic error when both python3 and python fail', async () => {
+ const runner: ProcessRunner = async () => {
+ return { exitCode: 1, stdout: '', stderr: 'No module named stdlib_error' };
+ };
+
+ const result = await executeMagpieSkills(runner, scriptPath, workspaceDir);
+ assert(result.text.includes('Unable to execute deterministic skills indexer'));
+ assert(result.text.includes('No module named stdlib_error'));
+ assert(result.text.includes('/magpie-utilities list-skills'));
+ });
+
+ test('prefers python error when python3 exits with 127 (not found)', async () => {
+ const runner: ProcessRunner = async (argv) => {
+ if (argv[0] === 'python3') {
+ return { exitCode: 127, stdout: '', stderr: 'python3: command not found' };
+ }
+ return { exitCode: 1, stdout: '', stderr: 'SyntaxError: invalid syntax in list_skills.py' };
+ };
+
+ const result = await executeMagpieSkills(runner, scriptPath, workspaceDir);
+ assert(result.text.includes('SyntaxError: invalid syntax in list_skills.py'));
+ });
+ });
+
+ describe('Claude Code hook registration and /magpie-skills command execution', () => {
+ test('registers and executes /magpie-skills returning stdout in session transcript', async ($: any, on: any) => {
+ const registeredCommands: any[] = [];
+ on('command.register', (_: any, e: any) => {
+ registeredCommands.push(e);
+ return { value: undefined };
+ });
+
+ on('session.start', () => ({ cwd: '/test/workspace' }));
+ on('session.root', () => ({ value: '/test/workspace' }));
+
+ on('process.run', (_: any, e: any) => {
+ return {
+ value: {
+ exitCode: 0,
+ stdout: MOCK_SKILLS_OUTPUT,
+ stderr: '',
+ },
+ };
+ });
+
+ await $.session.start({ surface: 'terminal', isInteractive: true, cwd: '/test/workspace' });
+
+ // Verify that magpie-skills was registered with immediate: true
+ const reg = registeredCommands.find((c) => c.name === 'magpie-skills');
+ assert(reg, 'Expected magpie-skills command to be registered');
+ assertStrictEqual(reg.name, 'magpie-skills');
+ assertStrictEqual(reg.immediate, true);
+
+ // Verify command execution
+ const answer = await $.command.run({ command: 'magpie-skills', args: '' });
+ assertStrictEqual(answer.text, MOCK_SKILLS_OUTPUT);
+ });
+
+ test('executes /magpie-skills with --verbose returning formatted output', async ($: any, on: any) => {
+ on('command.register', () => ({ value: undefined }));
+ on('session.start', () => ({ cwd: '/test/workspace' }));
+ on('session.root', () => ({ value: '/test/workspace' }));
+
+ let receivedArgv: readonly string[] = [];
+ on('process.run', (_: any, e: any) => {
+ receivedArgv = e.argv || [];
+ return {
+ value: {
+ exitCode: 0,
+ stdout: MOCK_VERBOSE_OUTPUT,
+ stderr: '',
+ },
+ };
+ });
+
+ await $.session.start({ surface: 'terminal', isInteractive: true, cwd: '/test/workspace' });
+
+ const answer = await $.command.run({ command: 'magpie-skills', args: '--verbose' });
+ assertStrictEqual(answer.text, MOCK_VERBOSE_OUTPUT);
+ assert(receivedArgv.includes('--verbose'), 'Expected argv to include --verbose');
+ assert(receivedArgv.includes('--root'), 'Expected argv to include --root');
+ });
+ });
+});
diff --git a/plugins/magpie-utilities/hooks/skills-command.ts b/plugins/magpie-utilities/hooks/skills-command.ts
new file mode 100644
index 00000000..667d56b3
--- /dev/null
+++ b/plugins/magpie-utilities/hooks/skills-command.ts
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+export interface ProcessRunResult {
+ exitCode: number;
+ stdout: string;
+ stderr: string;
+}
+
+export type ProcessRunner = (
+ argv: readonly string[],
+ init?: { cwd?: string; env?: Record<string, string>; timeoutMs?: number }
+) => Promise<ProcessRunResult>;
+
+const PROCESS_TIMEOUT_MS = 30_000;
+
+/**
+ * Joins path segments normalizing slashes without depending on node:path.
+ */
+export function joinPath(...segments: string[]): string {
+ const nonEmpties = segments.filter((s) => s && s.trim().length > 0);
+ if (nonEmpties.length === 0) return '.';
+
+ const isAbsolute = segments[0]?.startsWith('/') || segments[0]?.startsWith('\\');
+ const parts: string[] = [];
+
+ for (const seg of nonEmpties) {
+ const split = seg.split(/[/\\]+/);
+ for (const p of split) {
+ if (p === '..') {
+ if (parts.length > 0 && parts[parts.length - 1] !== '..') {
+ parts.pop();
+ } else if (!isAbsolute) {
+ parts.push('..');
+ }
+ } else if (p !== '.' && p !== '') {
+ parts.push(p);
+ }
+ }
+ }
+
+ const result = parts.join('/');
+ if (isAbsolute) {
+ return '/' + result;
+ }
+ return result || '.';
+}
+
+/**
+ * Executes list_skills.py deterministically via process runner (python3 or python).
+ */
+export async function executeMagpieSkills(
+ runProcess: ProcessRunner,
+ scriptPath: string,
+ workspaceDir: string,
+ rawArgs?: string
+): Promise<{ text: string }> {
+ const args = (rawArgs || '').trim();
+ const isHelp = /(?:--help|-h)(?:\s|$)/i.test(args);
+
+ let scriptArgs: string[];
+ if (isHelp) {
+ scriptArgs = [scriptPath, '--help'];
+ } else {
+ const isVerbose = /(?:--verbose|-v)(?:\s|$)/i.test(args);
+ const rootMatch = args.match(/(?:--root[=\s]+)(?:"([^"]+)"|'([^']+)'|([^\s]+))/i);
+ const rootCandidate = rootMatch
+ ? (rootMatch[1] ?? rootMatch[2] ?? rootMatch[3]).trim()
+ : '';
+ const targetRoot =
+ rootCandidate && !rootCandidate.startsWith('-') ? rootCandidate : workspaceDir;
+
+ scriptArgs = [scriptPath, '--root', targetRoot];
+ if (isVerbose) {
+ scriptArgs.push('--verbose');
+ }
+ }
+
+ const runInit = {
+ cwd: workspaceDir,
+ env: { PYTHONIOENCODING: 'utf-8' },
+ timeoutMs: PROCESS_TIMEOUT_MS,
+ };
+
+ // Try python3 first, fallback to python
+ let result: ProcessRunResult | undefined;
+ let executionError: Error | undefined;
+
+ try {
+ result = await runProcess(['python3', ...scriptArgs], runInit);
+ } catch (err: any) {
+ executionError = err instanceof Error ? err : new Error(String(err));
+ }
+
+ // If python3 failed to run (e.g. command not found) or returned non-zero, try python
+ if (!result || result.exitCode !== 0) {
+ try {
+ const fallbackResult = await runProcess(['python', ...scriptArgs], runInit);
+ if (fallbackResult.exitCode === 0 || !result || result.exitCode === 127) {
+ result = fallbackResult;
+ if (fallbackResult.exitCode === 0) {
+ executionError = undefined;
+ }
+ }
+ } catch (fallbackErr: any) {
+ if (!executionError) {
+ executionError = fallbackErr instanceof Error ? fallbackErr : new Error(String(fallbackErr));
+ }
+ }
+ }
+
+ if (result && result.exitCode === 0) {
+ return { text: result.stdout.trimEnd() };
+ }
+
+ const errorLines = [
+ '⚠️ Apache Magpie: Unable to execute deterministic skills indexer (`list_skills.py`).',
+ ];
+ if (result?.stderr?.trim()) {
+ errorLines.push(`Error: ${result.stderr.trim()}`);
+ } else if (executionError?.message) {
+ errorLines.push(`Error: ${executionError.message}`);
+ }
+ errorLines.push(
+ 'Please verify that Python 3 is installed and available on PATH, or invoke the full workflow via `/magpie-utilities list-skills`.'
+ );
+
+ return { text: errorLines.join('\n\n') };
+}
+
+/**
+ * Event hook registration entry point for Claude Code.
+ */
+export function register(on: any): void {
+ // 1. Hook session.start to register slash command
+ on('session.start', async ($: any, e: any, next: any) => {
+ try {
+ await $.command.register({
+ name: 'magpie-skills',
+ description: 'List installed Apache Magpie skills (zero-token)',
+ argumentHint: '[-v|--verbose] [--root <path>] [-h|--help]',
+ immediate: true,
+ });
+ } catch {
+ // Safe no-op on command register exception
+ }
+
+ if (typeof next === 'function') {
+ return next(e);
+ }
+ });
+
+ // 2. Hook command.run on magpie-skills
+ on('command.run', { command: 'magpie-skills' }, async ($: any, e: any) => {
+ try {
+ const workspaceDir = await $.session.root();
+ const pluginDir = $.plugin.root;
+ const scriptPath = joinPath(
+ pluginDir,
+ 'skills',
+ 'list-skills',
+ 'scripts',
+ 'list_skills.py'
+ );
+
+ return await executeMagpieSkills(
+ (argv, init) => $.process.run(argv, init),
+ scriptPath,
+ workspaceDir,
+ e.args
+ );
+ } catch (err: any) {
+ return {
+ text: `⚠️ Apache Magpie: Unexpected error executing /magpie-skills: ${err?.message || String(err)}`,
+ };
+ }
+ });
+}
+
+export default register;