الانتقال إلى المحتوى الرئيسي

ما الذي ستبنيه

يوضّح هذا الدليل عملية إنشاء قدرة جديدة من الصفر. ستحصل في نهايته على قدرة عاملة يكتشفها وولف فيش ويحمّلها ويتيحها لنموذج اللغة.
يستطيع وولف فيش فعل هذا كلّه بنفسه. تتيح له قدرة skills المدمجة أن يسرد قدراته ويبحث فيها ويفعّلها/يعطّلها ويحذفها ويؤلّفها أثناء التشغيل — فيتحوّل «افعل هذا في كل مرة» إلى مهارة قابلة لإعادة الاستخدام دون أن تلمس نظام الملفات. وهذا الدليل لحين تفضّل بناء واحدة بيدك.

تثبيت قدرة

لست مضطرًا للتعامل مع نظام الملفات. أسرع طريقة لإضافة قدرة هي إفلاتها على الإعدادات ← المُخيخ — أو النقر على حقل الاستيراد للتصفّح. تُقبل ثلاثة أشكال:

ملف SKILL.md واحد

إجراء Markdown مع واجهة YAML (الاسم، الوصف، والمحفّزات الاختيارية). يُستورد كـ مهارة نقية — دون أدوات للتثبيت.

مجلد قدرة

مجلد يحتوي على SKILL.md بالإضافة إلى plugin/ اختياري (index.mjs) وملف package.json. القدرة الكاملة — بأدواتها.

أرشيف ‎.zip

المجلد نفسه مضغوطًا (مثل ضغط في ماك). يُفكّ ضغطه إلى مجلد مؤقّت، ويُفحص، ثم يُضاف.
يُتحقَّق من كل عنصر قبل كتابة أي شيء على القرص، لذا لا يمكن لاستيراد فاشل أن يُثبّت جزئيًا أو يطمس قدرة موجودة. عند النجاح تُحدَّث القائمة وتظهر قدرتك فورًا — موسومة بـ غير معروف بدلًا من رسمي، لأنها قدرتك وليست مدمجة. وإذا فشل التحقق، تعرض اللوحة بالضبط ما حدث من خطأ (اسم مفقود، أو YAML غير صالح، أو مجلد plugin/ بلا ملف دخول، أو اسم مكرر…) في كتلة شيفرة، فتُصلحه وتعيد الإفلات. لإزالة قدرة مستوردة، انقر أيقونة سلة المهملات بجوارها وأكّد — فيُحذف مجلدها نظيفًا من brain/cerebellum/. القدرات الرسمية والمدمجة لا يوجد لها زر حذف ولا يمكن إزالتها بهذه الطريقة.
تفضّل الطرفية؟ أنشئ المجلد يدويًا بدلًا من ذلك — تُظهر الخطوات أدناه ذلك تمامًا. بعد تعديل الملفات يدويًا، انقر إعادة المزامنة في لوحة المُخيخ (أو أعد تشغيل وولف فيش) لالتقاط التغييرات. تستحق القواعد في التغليف للاستيراد والمشاركة الاتّباع في الحالتين — فهي ما يُبقي القدرة نظيفة وقابلة للنقل.

جرّبها الآن: قدرات نموذجية

تريد رؤية تدفّق الاستيراد قبل بناء قدرتك الخاصة؟ نزّل هذه الحزمة المكوّنة من ثلاث قدرات صغيرة جاهزة للتشغيل، ثم أفلت ملف .zip على الإعدادات ← المُخيخ. تُستورد الثلاث دفعة واحدة — يُتحقَّق منها وتُحمَّل في الحال، دون طرفية ودون كتابة أي شيفرة.

نزّل القدرات النموذجية (.zip)

coin-flip وdice-roller وcolor-mixer — ثلاث قدرات مكتفية بذاتها تستوردها بإفلاتٍ واحد وتراها حيّةً قيد العمل.
كل واحدة قدرة كاملة (SKILL.md + إضافة)، فبمجرد استيرادها يمكنك تشغيلها مباشرةً من المحادثة:
القدرةماذا تفعلجرّب أن تقول
coin-flipتقذف عملة عادلة«اقذف عملة»
dice-rollerترمي نردًا بأي عدد من الأوجه«ارمِ 2d20»
color-mixerتحوّل الألوان بين الصيغ«حوّل ‎#ff5733‎ إلى RGB»
تظهر موسومةً بـ غير معروف (فهي قدراتك لا قدرات مدمجة) ويمكن إزالتها في أي وقت بأيقونة سلة المهملات. افتحها في brain/cerebellum/ لترى بالضبط كيف تُبنى قدرة صغيرة — فهي بالشكل نفسه الذي عليه مثال الطقس أدناه.

الخطوة 1: أنشئ المجلد

mkdir -p ~/.wolffish/workspace/brain/cerebellum/my-capability

الخطوة 2: اكتب SKILL.md

أنشئ SKILL.md بواجهة YAML وتعليمات Markdown:
---
name: my-capability
description: What this capability does in one sentence
triggers:
  - keyword1
  - keyword2
tools:
  - name: my_tool
    description: What this tool does
    parameters:
      type: object
      properties:
        input:
          type: string
          description: The input to process
      required:
        - input
---

# My Capability

Instructions for the LLM on how to use this capability.

When the user asks about [topic], use the `my_tool` tool with the relevant input.
Always [specific behavior guideline].
Never [specific constraint].

الخطوة 3: اكتب الإضافة (اختياري)

إذا احتاجت قدرتك إلى كود مخصص، أنشئ plugin/index.mjs:
export default {
  name: 'my-capability',
  tools: ['my_tool'],

  async init(context) {
    // context.pluginDir — path to this plugin folder
    // context.workspaceRoot — path to ~/.wolffish/workspace/
  },

  async execute(toolName, args) {
    if (toolName === 'my_tool') {
      // Your logic here
      const result = doSomething(args.input)
      return { success: true, output: result }
    }
    return { success: false, output: '', error: `Unknown tool: ${toolName}` }
  },

  async destroy() {
    // Cleanup on shutdown
  }
}

الخطوة 4: أضف أنماط أمان (إذا لزم الأمر)

إذا كانت أداتك قادرة على إجراء عمليات خطرة، أضف أنماطًا إلى واجهة YAML لـ SKILL.md:
danger_patterns:
  - "dangerous_regex_here"
confirm_patterns:
  - "risky_but_allowed_regex"

الخطوة 5: اختبرها

أرسل رسالة تطابق مشغّلاتك. تحقق من:
  1. ملف تصحيح السياق (brain/prefrontal/.debug/) — هل أُدرج ملف SKILL.md الخاص بك في السياق؟
  2. سجل الأحداث (brain/corpus/) — هل أُطلق tool.called باسم أداتك؟
  3. سجل المهام (brain/motor/tasks/) — هل يُظهر ملف المهمة تنفيذًا ناجحًا؟

مثال حقيقي وعامل: قدرة الطقس

استخدم الشرح أعلاه عناصر نائبة. إليك قدرة كاملة يمكنك نسخها ولصقها اليوم وتعمل فورًا — تستدعي واجهة Open-Meteo المجانية التي لا تحتاج إلى مفتاح API ولا إلى تسجيل. تُظهر النمط كاملًا من البداية إلى النهاية: مخطط أداة حقيقي، ونداءان شبكيان متسلسلان (تحويل اسم المكان إلى إحداثيات ← جلب طقسه الحالي)، والتحقق من المدخلات، ومعالجة الأخطاء بأناقة، ونتيجة بلغة طبيعية نظيفة.

بنية المجلد

~/.wolffish/workspace/brain/cerebellum/weather/
├── SKILL.md
└── plugin/
    └── index.mjs
القدرات المدمجة مسبوقة بنقطة (.shell، .web-search) لتبقى مخفية عن ls العادي. قدراتك الخاصة لا تحتاج إلى النقطة — weather كافٍ وأسهل في الإيجاد. يُكتشف الاثنان بالطريقة نفسها.

SKILL.md

---
name: weather
description: Get the current weather for any city or place
triggers:
  - weather
  - temperature
  - forecast
  - how hot
  - how cold
  - is it raining
tools:
  - name: weather_get
    description: Get current weather conditions for a city, town, or place name.
    parameters:
      type: object
      properties:
        location:
          type: string
          description: City or place name, e.g. "Tokyo" or "Riyadh, Saudi Arabia"
        units:
          type: string
          enum: [celsius, fahrenheit]
          description: Temperature units. Defaults to celsius.
      required:
        - location
---

# Weather

When the user asks about the weather, temperature, or forecast for a place,
call `weather_get` with the location they named.

- If they don't specify units, use celsius (the API default).
- If they don't name a location, ask which city before calling the tool — never guess.
- Data comes from Open-Meteo (no API key). Report sky, temperature, humidity, and
  wind in one natural sentence.

plugin/index.mjs

// Real, working plugin — no API key required.
// Open-Meteo docs: https://open-meteo.com/en/docs

const GEOCODE = 'https://geocoding-api.open-meteo.com/v1/search'
const FORECAST = 'https://api.open-meteo.com/v1/forecast'

// WMO weather-interpretation codes → human text
const SKY = {
  0: 'clear sky', 1: 'mainly clear', 2: 'partly cloudy', 3: 'overcast',
  45: 'fog', 48: 'rime fog',
  51: 'light drizzle', 53: 'drizzle', 55: 'dense drizzle',
  61: 'light rain', 63: 'rain', 65: 'heavy rain',
  71: 'light snow', 73: 'snow', 75: 'heavy snow',
  80: 'rain showers', 81: 'rain showers', 82: 'violent rain showers',
  95: 'thunderstorm', 96: 'thunderstorm with hail', 99: 'severe thunderstorm'
}

export default {
  name: 'weather',
  tools: ['weather_get'],

  async execute(toolName, args) {
    if (toolName !== 'weather_get') {
      return { success: false, output: '', error: `Unknown tool: ${toolName}` }
    }

    const location = String(args.location ?? '').trim()
    if (!location) {
      return { success: false, output: '', error: 'Missing required "location" argument' }
    }
    const units = args.units === 'fahrenheit' ? 'fahrenheit' : 'celsius'

    try {
      // 1) Resolve the place name to coordinates
      const geoRes = await fetch(
        `${GEOCODE}?name=${encodeURIComponent(location)}&count=1&language=en&format=json`
      )
      if (!geoRes.ok) {
        return { success: false, output: '', error: `Geocoding failed (HTTP ${geoRes.status})` }
      }
      const place = (await geoRes.json()).results?.[0]
      if (!place) {
        return { success: false, output: '', error: `No location found for "${location}"` }
      }

      // 2) Fetch current conditions for those coordinates
      const res = await fetch(
        `${FORECAST}?latitude=${place.latitude}&longitude=${place.longitude}` +
        `&current=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code` +
        `&temperature_unit=${units}`
      )
      if (!res.ok) {
        return { success: false, output: '', error: `Forecast failed (HTTP ${res.status})` }
      }

      const data = await res.json()
      const c = data.current
      const u = data.current_units
      const sky = SKY[c.weather_code] ?? `code ${c.weather_code}`
      const where = [place.name, place.admin1, place.country].filter(Boolean).join(', ')

      return {
        success: true,
        output:
          `Weather in ${where}: ${sky}, ${c.temperature_2m}${u.temperature_2m}, ` +
          `humidity ${c.relative_humidity_2m}${u.relative_humidity_2m}, ` +
          `wind ${c.wind_speed_10m} ${u.wind_speed_10m}.`
      }
    } catch (err) {
      return { success: false, output: '', error: `Weather lookup failed: ${err.message}` }
    }
  }
}

لماذا لا توجد danger_patterns

تقرأ weather_get بيانات عامة عبر HTTPS فقط — لا يمكنها حذف أو إنفاق أو الكتابة فوق أي شيء. الأدوات للقراءة فقط مثل هذه لا تحتاج إلى أي أنماط أمان. احتفظ بأنماط danger_patterns / confirm_patterns للأدوات التي قد تسبب ضررًا فعليًا (انظر أنماط الأمان).

جرّبها

أعد تشغيل وولف فيش ليحمّل المخيخ المجلد الجديد، ثم اسأل:
“ما حالة الطقس في طوكيو؟”
سترى الوكيل يستدعي weather_get ويردّ بشيء مثل “Weather in Tokyo, Tokyo, Japan: partly cloudy, 22.4°C, humidity 61%, wind 9.3 km/h.” تتبّع النداء في brain/corpus/<today>.log.md للتأكد من الوسائط والمخرجات.
هذا هو القالب لأي تكامل للقراءة فقط تقريبًا. استبدل نداءَي fetch بواجهة أخرى — Hacker News، أو أسعار العملات، أو خادمك الخاص — مع الحفاظ على شكل تحقّق ← نداء ← تنسيق ← إرجاع، وستحصل على قدرة جديدة في دقائق.

مهارة نقية مقابل إضافة: متى تستخدم أيًا منهما

استخدم مهارة نقية عندما يمكن إنجاز قدرتك باستخدام أدوات موجودة (مثل shell_exec أو file_read)، أو عندما تريد تشكيل سلوك الوكيل بدون إضافة أدوات جديدة. تُحقن تعليمات SKILL.md في قسم <skills> في الموجّه عند تفعيلها. استخدم إضافة عندما تحتاج إلى منطق مخصص لا يمكن التعبير عنه بأوامر شل أو عمليات ملفات — استدعاءات واجهات برمجية، ومعالجة بيانات، وبروتوكولات مخصصة، إلخ.
ابدأ بمهارة نقية. إذا وجدت أن نموذج اللغة يكافح مع سلاسل أدوات معقدة، رقّ إلى إضافة. المهارات النقية أبسط في الكتابة والتصحيح والمشاركة.

المهارات النقية الدائمة

بعض المهارات النقية يجب أن تنطبق على كل رسالة — انضباط التخطيط، تنسيق المخرجات، قيود الأمان. استخدم المشغّل الشامل "*":
---
name: planning
description: Think before acting.
triggers:
  - "*"
tools: []
---

# Planning

Before executing any multi-step task, state your plan first...
تُحقن المهارات الدائمة قبل المهارات المطابقة بالكلمات المفتاحية ولا تُحتسب ضمن حدّ الـ 3. يمكن لرسالة واحدة تفعيل حتى 3 مهارات مطابقة بالكلمات المفتاحية بالإضافة إلى أي عدد من المهارات الدائمة.

قائمة فحص القدرة

قبل شحن قدرتك:
  • يحتوي SKILL.md على مشغّلات دقيقة (اختبر بصيغ مختلفة)
  • أوصاف معاملات الأدوات واضحة (يقرؤها نموذج اللغة)
  • أنماط الخطر تغطي كل العمليات المدمرة
  • أنماط التأكيد تغطي العمليات الخطرة لكنها مشروعة
  • تتعامل الإضافة مع الأخطاء بأناقة (تُرجع ToolResult بقيمة success: false)
  • متن Markdown يحتوي على تعليمات واضحة ومحددة
  • تم الاختبار مع نماذج سحابية ومحلية على حد سواء

التغليف للاستيراد والمشاركة

سواء أضاف أحدهم قدرتك كمجلد أو كملف .zip، يُجري المستورد الفحوص نفسها. واستيفاؤها يُبقي قدرتك نظيفة وقابلة للنقل وآمنة للمشاركة:
  • ملف SKILL.md واحد فقط. يجده المستورد في الجذر أو حتى ثلاثة مجلدات في العمق، لذا يعمل ضغط مجلد (my-skill/SKILL.md) مباشرةً. ووجود صفر أو أكثر من ملف SKILL.md يُرفض.
  • واجهة صالحة. يجب أن تبدأ بكتلة --- تُحلَّل كـ YAML ولها name غير فارغ. والحقول الاختيارية تُفحص بنيتها عند وجودها: يجب أن يكون triggers وrequires قوائم نصوص، وكل عنصر في tools يحتاج إلى name. انظر مرجع SKILL.md.
  • شيء لتحميله. ملف SKILL.md بمتن فارغ وبلا أدوات لا يفعل شيئًا، ويُرفض — امنحه متنًا أو أدوات أو كليهما.
  • الإضافة تحتاج ملف دخول. إذا شحنت مجلد plugin/، فيجب أن يحتوي على index.mjs أو index.js أو index.cjs. وإعلان tools في مجلد/zip بلا plugin/ يُرفض — إذ لن يكون للأدوات ما يشغّلها.
  • اسم فريد. لا يمكن أن يتعارض name مع قدرة لديك بالفعل (مدمجة أو مستوردة). ويُشتق اسم المجلد على القرص من name بتحويله إلى صيغة سهلة (My Cool Skillmy-cool-skill).
  • تُزال المخلفات تلقائيًا. تُتخطّى node_modules/ و.git/ و.DS_Store و__MACOSX/ عند الاستيراد — لا داعي لإزالتها أولًا، واترك node_modules/ خارج ملف zip لإبقائه صغيرًا.
  • حدود الحجم. SKILL.md ≤ 1 ميغابايت، والحمولة الكلية ≤ 50 ميغابايت، و≤ 5000 ملف. وتُستخرج ملفات zip مع حماية من اجتياز المسارات.
  • التبعيات تُثبَّت عند الحاجة. أي شيء في requires (أدوات النظام) أو في package.json (حزم npm) يُثبَّت أول مرة تعمل فيها القدرة، لا عند الاستيراد — فيبقى الاستيراد سريعًا وآمنًا دون اتصال.
أبسط طريقة لتسليم قدرة لأحدهم هي ضغط المجلد وإرساله — يُفلت ملف .zip على لوحة المُخيخ لديه فتصبح فعّالة. وللتوزيع المستمر، ادفعها إلى git بدلًا من ذلك (انظر قدرات المجتمع).