[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6HXAT2SJF14E7P6B4PJWDHA":3,"skill-same:01M6HXAT2SJF14E7P6B4PJWDHA":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":50,"repositoryHtmlUrl":51,"repositoryHomepageUrl":6,"ownerHtmlUrl":52,"ownerBlogUrl":53,"ownerDescription":6,"updatedAt":54,"install":55,"resources":68},"01M6HXAT2SJF14E7P6B4PJWDHA","panel-data-resilient",null,"Create resilient, data-driven dashboard panels with integrated API handling and UI components, combining circuit breaker patterns with vanilla TypeScript panel architecture.","HKUDS\u002FAgentPilot","HKUDS","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F118165258?v=4","01M6HW8VRNZP64G0P4MVJW2KKQ","✨Data Intelligence Lab@HKU✨","01M6HW8VWZVHBRHYH3T330PFWV",8024,"MIT","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fexamples\u002Fmy-daily-monitor\u002Fskills\u002Fpanel-data-resilient\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"panel data resilient","panel","data","Circuit Breaker","data-driven","circuit","with","ui components","panels","driven","typescript","patterns","api","ui","create","architecture","components","dashboard","2026-11-10 10:12:30",false,"---\nname: panel-data-resilient\ndescription: Create resilient, data-driven dashboard panels with integrated API handling and UI components, combining circuit breaker patterns with vanilla TypeScript panel architecture.\n---\n\n# Resilient Panel Pattern\n\nThis skill merges the best of `data-service` and `panel-component` to create panels that are both resilient to API failures and easy to implement. Each panel manages its own data fetching with built-in circuit breaker patterns and renders content using vanilla TypeScript.\n\n## Architecture Overview\n\n1. **Service Module**: Each panel has a dedicated service module for data fetching, using circuit breakers for resilience.\n2. **Panel Class**: Extends a base `Panel` class with built-in loading\u002Ferror states and content rendering.\n3. **Data Flow**: Panels automatically fetch data, handle errors, and retry as needed.\n\n## Implementation\n\n### 1. Circuit Breaker Utility\n\nCreate `src\u002Futils\u002Fcircuit-breaker.ts` (same as in `data-service` but with enhanced logging):\n\n```typescript\ninterface CircuitState {\n  failures: number;\n  cooldownUntil: number;\n}\n\ninterface CacheEntry\u003CT> {\n  data: T;\n  timestamp: number;\n}\n\nexport interface CircuitBreakerOptions {\n  name: string;\n  maxFailures?: number;\n  cooldownMs?: number;\n  cacheTtlMs?: number;\n}\n\nexport class CircuitBreaker\u003CT> {\n  private state: CircuitState = { failures: 0, cooldownUntil: 0 };\n  private cache: CacheEntry\u003CT> | null = null;\n  private name: string;\n  private maxFailures: number;\n  private cooldownMs: number;\n  private cacheTtlMs: number;\n\n  constructor(options: CircuitBreakerOptions) {\n    this.name = options.name;\n    this.maxFailures = options.maxFailures ?? 2;\n    this.cooldownMs = options.cooldownMs ?? 5 * 60 * 1000;  \u002F\u002F 5 minutes\n    this.cacheTtlMs = options.cacheTtlMs ?? 10 * 60 * 1000; \u002F\u002F 10 minutes\n  }\n\n  isOnCooldown(): boolean {\n    if (Date.now() \u003C this.state.cooldownUntil) return true;\n    if (this.state.cooldownUntil > 0) {\n      this.state = { failures: 0, cooldownUntil: 0 };\n    }\n    return false;\n  }\n\n  getCached(): T | null {\n    if (this.cache && Date.now() - this.cache.timestamp \u003C this.cacheTtlMs) {\n      return this.cache.data;\n    }\n    return null;\n  }\n\n  recordSuccess(data: T): void {\n    this.state = { failures: 0, cooldownUntil: 0 };\n    this.cache = { data, timestamp: Date.now() };\n  }\n\n  recordFailure(error?: string): void {\n    this.state.failures++;\n    if (this.state.failures >= this.maxFailures) {\n      this.state.cooldownUntil = Date.now() + this.cooldownMs;\n      console.warn(`[${this.name}] Cooldown for ${this.cooldownMs \u002F 1000}s`);\n    }\n  }\n\n  async execute\u003CR extends T>(fn: () => Promise\u003CR>, defaultValue: R): Promise\u003CR> {\n    if (this.isOnCooldown()) {\n      const cached = this.getCached();\n      return (cached as R) ?? defaultValue;\n    }\n    const cached = this.getCached();\n    if (cached !== null) return cached as R;\n    try {\n      const result = await fn();\n      this.recordSuccess(result);\n      return result;\n    } catch (e) {\n      console.error(`[${this.name}] Failed:`, e);\n      this.recordFailure(String(e));\n      return defaultValue;\n    }\n  }\n}\n\nexport function createCircuitBreaker\u003CT>(options: CircuitBreakerOptions): CircuitBreaker\u003CT> {\n  return new CircuitBreaker\u003CT>(options);\n}\n```\n\n### 2. Base Panel Class\n\nCreate `src\u002Fcomponents\u002FPanel.ts` with enhanced error handling and retry logic:\n\n```typescript\nexport interface PanelOptions {\n  id: string;\n  title: string;\n  showCount?: boolean;\n  className?: string;\n}\n\nexport class Panel {\n  protected element: HTMLElement;\n  protected content: HTMLElement;\n  protected header: HTMLElement;\n  protected countEl: HTMLElement | null = null;\n  protected panelId: string;\n  private _fetching = false;\n\n  constructor(options: PanelOptions) {\n    this.panelId = options.id;\n    this.element = document.createElement('div');\n    this.element.className = `panel ${options.className || ''}`;\n    this.element.dataset.panel = options.id;\n\n    \u002F\u002F Header\n    this.header = document.createElement('div');\n    this.header.className = 'panel-header';\n\n    const headerLeft = document.createElement('div');\n    headerLeft.className = 'panel-header-left';\n\n    const title = document.createElement('span');\n    title.className = 'panel-title';\n    title.textContent = options.title;\n    headerLeft.appendChild(title);\n    this.header.appendChild(headerLeft);\n\n    \u002F\u002F Count badge (optional)\n    if (options.showCount) {\n      this.countEl = document.createElement('span');\n      this.countEl.className = 'panel-count';\n      this.countEl.textContent = '0';\n      this.header.appendChild(this.countEl);\n    }\n\n    \u002F\u002F Content area\n    this.content = document.createElement('div');\n    this.content.className = 'panel-content';\n    this.content.id = `${options.id}Content`;\n\n    this.element.appendChild(this.header);\n    this.element.appendChild(this.content);\n    this.showLoading();\n  }\n\n  public getElement(): HTMLElement { return this.element; }\n\n  public showLoading(message = 'Loading...'): void {\n    this.content.innerHTML = `\n      \u003Cdiv class=\"panel-loading\">\n        \u003Cdiv class=\"panel-loading-spinner\">\u003C\u002Fdiv>\n        \u003Cdiv class=\"panel-loading-text\">${message}\u003C\u002Fdiv>\n      \u003C\u002Fdiv>`;\n  }\n\n  public showError(message = 'Failed to load', onRetry?: () => void): void {\n    this.content.innerHTML = `\n      \u003Cdiv class=\"panel-error-state\">\n        \u003Cdiv class=\"panel-error-msg\">${message}\u003C\u002Fdiv>\n        ${onRetry ? '\u003Cbutton class=\"panel-retry-btn\" data-panel-retry>Retry\u003C\u002Fbutton>' : ''}\n      \u003C\u002Fdiv>`;\n    if (onRetry) {\n      this.content.querySelector('[data-panel-retry]')?.addEventListener('click', onRetry);\n    }\n  }\n\n  public setContent(html: string): void {\n    this.content.innerHTML = html;\n  }\n\n  public setCount(count: number): void {\n    if (this.countEl) this.countEl.textContent = count.toString();\n  }\n\n  public show(): void { this.element.classList.remove('hidden'); }\n  public hide(): void { this.element.classList.add('hidden'); }\n\n  protected setFetching(v: boolean): void { this._fetching = v; }\n  protected get isFetching(): boolean { return this._fetching; }\n\n  public destroy(): void {\n    this.element.remove();\n  }\n}\n```\n\n### 3. Example: StockPanel with Integrated Service\n\n```typescript\nimport { Panel } from '.\u002FPanel';\nimport { createCircuitBreaker } from '..\u002Futils\u002Fcircuit-breaker';\n\ninterface StockQuote {\n  symbol: string;\n  name: string;\n  price: number | null;\n  change: number | null;\n  sparkline?: number[];\n}\n\nconst breaker = createCircuitBreaker\u003CStockQuote[]>({\n  name: 'StockMarket',\n  cacheTtlMs: 60_000,\n});\n\nexport class StockPanel extends Panel {\n  private refreshTimer: ReturnType\u003Ctypeof setInterval> | null = null;\n\n  constructor() {\n    super({ id: 'stocks', title: 'Stock Market', showCount: true });\n    this.fetchData();\n    this.refreshTimer = setInterval(() => this.fetchData(), 60_000);\n  }\n\n  private async fetchData(): Promise\u003Cvoid> {\n    if (this.isFetching) return;\n    this.setFetching(true);\n    try {\n      const quotes = await breaker.execute(async () => {\n        const resp = await fetch('\u002Fapi\u002Fstocks');\n        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n        return resp.json();\n      }, []);\n      this.render(quotes);\n      this.setCount(quotes.length);\n    } catch (err) {\n      this.showError('Failed to load stock data', () => this.fetchData());\n    } finally {\n      this.setFetching(false);\n    }\n  }\n\n  private render(quotes: StockQuote[]): void {\n    const rows = quotes.map(q => `\n      \u003Cdiv class=\"stock-row\">\n        \u003Cspan class=\"stock-symbol\">${q.symbol}\u003C\u002Fspan>\n        \u003Cspan class=\"stock-name\">${q.name}\u003C\u002Fspan>\n        \u003Cspan class=\"stock-price\">${q.price != null ? '$' + q.price.toFixed(2) : '—'}\u003C\u002Fspan>\n        \u003Cspan class=\"stock-change ${(q.change ?? 0) >= 0 ? 'positive' : 'negative'}\">\n          ${q.change != null ? (q.change >= 0 ? '+' : '') + q.change.toFixed(2) + '%' : '—'}\n        \u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    `).join('');\n\n    this.setContent(`\u003Cdiv class=\"stock-list\">${rows}\u003C\u002Fdiv>`);\n  }\n\n  public override destroy(): void {\n    if (this.refreshTimer) clearInterval(this.refreshTimer);\n    super.destroy();\n  }\n}\n```\n\n## Key Patterns\n\n1. **Integrated Data Handling**: Each panel manages its own data fetching with built-in circuit breakers.\n2. **Resilient UI**: Automatic retry logic and graceful degradation when APIs fail.\n3. **Simple Implementation**: Just extend `Panel` and implement `fetchData()` and `render()`.\n4. **Type Safety**: Strongly typed data interfaces throughout.\n","# Resilient Panel Pattern\n\nThis skill merges the best of `data-service` and `panel-component` to create panels that are both resilient to API failures and easy to implement. Each panel manages its own data fetching with built-in circuit breaker patterns and renders content using vanilla TypeScript.\n\n## Architecture Overview\n\n1. **Service Module**: Each panel has a dedicated service module for data fetching, using circuit breakers for resilience.\n2. **Panel Class**: Extends a base `Panel` class with built-in loading\u002Ferror states and content rendering.\n3. **Data Flow**: Panels automatically fetch data, handle errors, and retry as needed.\n\n## Implementation\n\n### 1. Circuit Breaker Utility\n\nCreate `src\u002Futils\u002Fcircuit-breaker.ts` (same as in `data-service` but with enhanced logging):\n\n```typescript\ninterface CircuitState {\n  failures: number;\n  cooldownUntil: number;\n}\n\ninterface CacheEntry\u003CT> {\n  data: T;\n  timestamp: number;\n}\n\nexport interface CircuitBreakerOptions {\n  name: string;\n  maxFailures?: number;\n  cooldownMs?: number;\n  cacheTtlMs?: number;\n}\n\nexport class CircuitBreaker\u003CT> {\n  private state: CircuitState = { failures: 0, cooldownUntil: 0 };\n  private cache: CacheEntry\u003CT> | null = null;\n  private name: string;\n  private maxFailures: number;\n  private cooldownMs: number;\n  private cacheTtlMs: number;\n\n  constructor(options: CircuitBreakerOptions) {\n    this.name = options.name;\n    this.maxFailures = options.maxFailures ?? 2;\n    this.cooldownMs = options.cooldownMs ?? 5 * 60 * 1000;  \u002F\u002F 5 minutes\n    this.cacheTtlMs = options.cacheTtlMs ?? 10 * 60 * 1000; \u002F\u002F 10 minutes\n  }\n\n  isOnCooldown(): boolean {\n    if (Date.now() \u003C this.state.cooldownUntil) return true;\n    if (this.state.cooldownUntil > 0) {\n      this.state = { failures: 0, cooldownUntil: 0 };\n    }\n    return false;\n  }\n\n  getCached(): T | null {\n    if (this.cache && Date.now() - this.cache.timestamp \u003C this.cacheTtlMs) {\n      return this.cache.data;\n    }\n    return null;\n  }\n\n  recordSuccess(data: T): void {\n    this.state = { failures: 0, cooldownUntil: 0 };\n    this.cache = { data, timestamp: Date.now() };\n  }\n\n  recordFailure(error?: string): void {\n    this.state.failures++;\n    if (this.state.failures >= this.maxFailures) {\n      this.state.cooldownUntil = Date.now() + this.cooldownMs;\n      console.warn(`[${this.name}] Cooldown for ${this.cooldownMs \u002F 1000}s`);\n    }\n  }\n\n  async execute\u003CR extends T>(fn: () => Promise\u003CR>, defaultValue: R): Promise\u003CR> {\n    if (this.isOnCooldown()) {\n      const cached = this.getCached();\n      return (cached as R) ?? defaultValue;\n    }\n    const cached = this.getCached();\n    if (cached !== null) return cached as R;\n    try {\n      const result = await fn();\n      this.recordSuccess(result);\n      return result;\n    } catch (e) {\n      console.error(`[${this.name}] Failed:`, e);\n      this.recordFailure(String(e));\n      return defaultValue;\n    }\n  }\n}\n\nexport function createCircuitBreaker\u003CT>(options: CircuitBreakerOptions): CircuitBreaker\u003CT> {\n  return new CircuitBreaker\u003CT>(options);\n}\n```\n\n### 2. Base Panel Class\n\nCreate `src\u002Fcomponents\u002FPanel.ts` with enhanced error handling and retry logic:\n\n```typescript\nexport interface PanelOptions {\n  id: string;\n  title: string;\n  showCount?: boolean;\n  className?: string;\n}\n\nexport class Panel {\n  protected element: HTMLElement;\n  protected content: HTMLElement;\n  protected header: HTMLElement;\n  protected countEl: HTMLElement | null = null;\n  protected panelId: string;\n  private _fetching = false;\n\n  constructor(options: PanelOptions) {\n    this.panelId = options.id;\n    this.element = document.createElement('div');\n    this.element.className = `panel ${options.className || ''}`;\n    this.element.dataset.panel = options.id;\n\n    \u002F\u002F Header\n    this.header = document.createElement('div');\n    this.header.className = 'panel-header';\n\n    const headerLeft = document.createElement('div');\n    headerLeft.className = 'panel-header-left';\n\n    const title = document.createElement('span');\n    title.className = 'panel-title';\n    title.textContent = options.title;\n    headerLeft.appendChild(title);\n    this.header.appendChild(headerLeft);\n\n    \u002F\u002F Count badge (optional)\n    if (options.showCount) {\n      this.countEl = document.createElement('span');\n      this.countEl.className = 'panel-count';\n      this.countEl.textContent = '0';\n      this.header.appendChild(this.countEl);\n    }\n\n    \u002F\u002F Content area\n    this.content = document.createElement('div');\n    this.content.className = 'panel-content';\n    this.content.id = `${options.id}Content`;\n\n    this.element.appendChild(this.header);\n    this.element.appendChild(this.content);\n    this.showLoading();\n  }\n\n  public getElement(): HTMLElement { return this.element; }\n\n  public showLoading(message = 'Loading...'): void {\n    this.content.innerHTML = `\n      \u003Cdiv class=\"panel-loading\">\n        \u003Cdiv class=\"panel-loading-spinner\">\u003C\u002Fdiv>\n        \u003Cdiv class=\"panel-loading-text\">${message}\u003C\u002Fdiv>\n      \u003C\u002Fdiv>`;\n  }\n\n  public showError(message = 'Failed to load', onRetry?: () => void): void {\n    this.content.innerHTML = `\n      \u003Cdiv class=\"panel-error-state\">\n        \u003Cdiv class=\"panel-error-msg\">${message}\u003C\u002Fdiv>\n        ${onRetry ? '\u003Cbutton class=\"panel-retry-btn\" data-panel-retry>Retry\u003C\u002Fbutton>' : ''}\n      \u003C\u002Fdiv>`;\n    if (onRetry) {\n      this.content.querySelector('[data-panel-retry]')?.addEventListener('click', onRetry);\n    }\n  }\n\n  public setContent(html: string): void {\n    this.content.innerHTML = html;\n  }\n\n  public setCount(count: number): void {\n    if (this.countEl) this.countEl.textContent = count.toString();\n  }\n\n  public show(): void { this.element.classList.remove('hidden'); }\n  public hide(): void { this.element.classList.add('hidden'); }\n\n  protected setFetching(v: boolean): void { this._fetching = v; }\n  protected get isFetching(): boolean { return this._fetching; }\n\n  public destroy(): void {\n    this.element.remove();\n  }\n}\n```\n\n### 3. Example: StockPanel with Integrated Service\n\n```typescript\nimport { Panel } from '.\u002FPanel';\nimport { createCircuitBreaker } from '..\u002Futils\u002Fcircuit-breaker';\n\ninterface StockQuote {\n  symbol: string;\n  name: string;\n  price: number | null;\n  change: number | null;\n  sparkline?: number[];\n}\n\nconst breaker = createCircuitBreaker\u003CStockQuote[]>({\n  name: 'StockMarket',\n  cacheTtlMs: 60_000,\n});\n\nexport class StockPanel extends Panel {\n  private refreshTimer: ReturnType\u003Ctypeof setInterval> | null = null;\n\n  constructor() {\n    super({ id: 'stocks', title: 'Stock Market', showCount: true });\n    this.fetchData();\n    this.refreshTimer = setInterval(() => this.fetchData(), 60_000);\n  }\n\n  private async fetchData(): Promise\u003Cvoid> {\n    if (this.isFetching) return;\n    this.setFetching(true);\n    try {\n      const quotes = await breaker.execute(async () => {\n        const resp = await fetch('\u002Fapi\u002Fstocks');\n        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n        return resp.json();\n      }, []);\n      this.render(quotes);\n      this.setCount(quotes.length);\n    } catch (err) {\n      this.showError('Failed to load stock data', () => this.fetchData());\n    } finally {\n      this.setFetching(false);\n    }\n  }\n\n  private render(quotes: StockQuote[]): void {\n    const rows = quotes.map(q => `\n      \u003Cdiv class=\"stock-row\">\n        \u003Cspan class=\"stock-symbol\">${q.symbol}\u003C\u002Fspan>\n        \u003Cspan class=\"stock-name\">${q.name}\u003C\u002Fspan>\n        \u003Cspan class=\"stock-price\">${q.price != null ? '$' + q.price.toFixed(2) : '—'}\u003C\u002Fspan>\n        \u003Cspan class=\"stock-change ${(q.change ?? 0) >= 0 ? 'positive' : 'negative'}\">\n          ${q.change != null ? (q.change >= 0 ? '+' : '') + q.change.toFixed(2) + '%' : '—'}\n        \u003C\u002Fspan>\n      \u003C\u002Fdiv>\n    `).join('');\n\n    this.setContent(`\u003Cdiv class=\"stock-list\">${rows}\u003C\u002Fdiv>`);\n  }\n\n  public override destroy(): void {\n    if (this.refreshTimer) clearInterval(this.refreshTimer);\n    super.destroy();\n  }\n}\n```\n\n## Key Patterns\n\n1. **Integrated Data Handling**: Each panel manages its own data fetching with built-in circuit breakers.\n2. **Resilient UI**: Automatic retry logic and graceful degradation when APIs fail.\n3. **Simple Implementation**: Just extend `Panel` and implement `fetchData()` and `render()`.\n4. **Type Safety**: Strongly typed data interfaces throughout.\n",{"name":5,"description":7},"main","6b84b0689ca0d9e7b1e4061188ff6b60758e0f8a","openspace\u002Fexamples\u002Fmy-daily-monitor\u002Fskills\u002Fpanel-data-resilient",941,"https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot","https:\u002F\u002Fgithub.com\u002FHKUDS","https:\u002F\u002Fsites.google.com\u002Fview\u002Fchaoh","2026-11-12 22:26:39",{"source":8,"skillName":5,"commands":56,"prompt":66,"zipAvailable":67},[57,60,63],{"tool":58,"command":59},"npx","npx skills add HKUDS\u002FAgentPilot --skill panel-data-resilient",{"tool":61,"command":62},"bunx","bunx skills add HKUDS\u002FAgentPilot --skill panel-data-resilient",{"tool":64,"command":65},"pnpm","pnpm dlx skills add HKUDS\u002FAgentPilot --skill panel-data-resilient","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fexamples\u002Fmy-daily-monitor\u002Fskills\u002Fpanel-data-resilient\u002FSKILL.md",true,[69],{"path":70,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":73,"sourceHtmlUrl":74},".skill_id","OTHER","其他",33,"https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fexamples\u002Fmy-daily-monitor\u002Fskills\u002Fpanel-data-resilient\u002F.skill_id",[76,95,114,139,163,185],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":42},"01M6HYB297NDABRERJRVG0K8R0","agentpilot","Set up AgentPilot and use its single entry to prepare missing inputs and run configured RSI experiments. Use when the user requests AgentPilot installation or work with AgentPilot.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fplugins\u002Fagentpilot\u002Fskills\u002Fagentpilot\u002FSKILL.md",[],[78,83,84,30,85,86,87,88,89,90,91,92,93,94],"prepare","requests","missing","installation","rsi","up","use","user","work","SET","run","experiments",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":42},"01M6HYAYWXTTE1J7DJM0MFNQ4H","remember","Review auto-memory entries and propose promotions to OPENSPACE.md, OPENSPACE.local.md, or retained auto-memory topics; detect duplicates, stale facts, conflicts, and ambiguous entries before making changes.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fskills\u002Fremember\u002FSKILL.md",[],[97,102,103,104,105,106,107,108,109,110,111,112,113],"memory","review","auto","openspace","changes","facts","detect","entries","local","conflicts","stale","duplicates",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":125,"publishedAt":42},"01M6HYAVMPM4SBDV60R2B5HPV8","skill-discovery","Search reusable OpenSpace skills locally, and browse cloud packages step by step when a cloud skill may help. Reusing proven skills saves tokens, improves reliability, and extends your capabilities beyond built-in tools.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fhost_skills\u002Fskill-discovery\u002FSKILL.md",[120],{"categoryNo":121,"categoryName":122,"taxonomyType":123,"taxonomyTypeDesc":124},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[126,127,128,105,129,130,131,132,133,134,135,136,137,138],"skill discovery","discovery","reusable","step by step","tokens","packages","help","cloud","reliability","capabilities","browse","search","Tools",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":146,"publishedAt":42},"01M6HYAR811B3RB0M50RBEDGN6","delegate-task","Delegate tasks to OpenSpace — a full-stack autonomous worker for coding, DevOps, web research, and desktop automation, backed by an extensive MCP tool and skill library. Skills auto-improve through use, reducing token consumption over time. A cloud community lets agents share and collectively evolve reusable skills.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fhost_skills\u002Fdelegate-task\u002FSKILL.md",[145],{"categoryNo":121,"categoryName":122,"taxonomyType":123,"taxonomyTypeDesc":124},[147,148,149,105,150,151,152,153,154,128,104,89,155,156,157,158,159,160,161,162],"delegate","task","delegate task","time","improve","desktop automation","worker","community","autonomous","tool","stack","for","share","tasks","automation","MCP",{"skillNo":164,"name":165,"nameZh":6,"description":166,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":169,"publishedAt":42},"01M6HYAM9FSGV6E52AYENDQXYH","write-special-chars-ts-check","Workaround for write_file failures caused by special characters (apostrophes, backticks, template literals) using a Python heredoc script, plus a reliable TypeScript type-checking step that filters output to only the newly-written files to avoid being misled by pre-existing errors.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fwrite-special-chars-ts-check\u002FSKILL.md",[],[170,171,172,173,174,175,176,177,178,179,34,180,181,182,158,183,184],"write special chars ts check","write","check","type","files","filters","only","template","file","python","output","Existing","type-checking","errors","script",{"skillNo":186,"name":187,"nameZh":6,"description":188,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":189,"categories":190,"keywords":191,"publishedAt":42},"01M6HYAH05HGV7SVWSVR8PJRQD","write-special-chars-readback","Workaround for write_file failures caused by special characters (apostrophes, backticks, template literals) using a Python heredoc script, with mandatory post-write verification via read_file and cleanup of \u002Ftmp helper scripts.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fwrite-special-chars-readback\u002FSKILL.md",[],[192,193,171,30,158,184,194,195,196,197,198,177,178,199,200,179],"write special chars readback","readback","helper","post","via","scripts","read","verification","cleanup"]