Stuck logo

Stuck

OrganizationPopular
QwenLM
stuck

Diagnose frozen, stuck, or slow Qwen Code sessions on this machine. Scans for problematic processes, high CPU/memory usage, hung subprocesses, and debug logs. Use /stuck or /stuck <PID> to focus on a specific process.

Overview

PublisherQwenLM
Repositoryqwen-code
Skill namestuck
Stars
27.9K
Forks
3.1K
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by QwenLM on GitHub. Read the source before you install it.

Installation

Install the Stuck AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/QwenLM/qwen-code.git /tmp/qwen-code
mkdir -p .claude/skills
cp -r /tmp/qwen-code/packages/core/src/skills/bundled/stuck .claude/skills/stuck
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Stuck in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Stuck on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Stuck is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

/stuck — diagnose frozen/slow Qwen Code sessions

The user thinks another Qwen Code session on this machine is frozen, stuck, or very slow. Investigate and present a diagnostic report.

What to look for

Scan for other Qwen Code processes (excluding the current one — exclude the PID you see running this prompt). Since Qwen Code is a Node.js CLI (#!/usr/bin/env node), the process name (comm column) is always node (or bun if run with Bun). Identify Qwen Code sessions by looking at the command column for a script path inside a directory whose name starts with qwen-code (matches qwen-code/, qwen-code-dev/, worktree clones, etc.) — anchored to the start of the path or after / so unrelated names like analyze-qwen-code/ don't false-match — or a bin invocation ending in /qwen (the global symlink). Avoid loose qwen-code substring matching: it false-positives on plugin brokers that merely pass a qwen-code path as --cwd.

Signs of a stuck session:

  • High CPU (>=90%) sustained — likely an infinite loop. Sample twice, 1-2s apart, to confirm it's not a transient spike.
  • Process state D / U (uninterruptible sleep) — often an I/O hang. Linux uses D, macOS/BSD uses U. The state column in ps output; first character matters (ignore modifiers like +, s, <).
  • Process state T (stopped) — user probably hit Ctrl+Z by accident.
  • Process state Z (zombie) — parent isn't reaping.
  • Very high RSS (>=4GB) — possible memory leak making the session sluggish.
  • State S with low CPU — the most common hang signature: a hung HTTPS request to the model API. Not a process-level red flag on its own, but combined with the user reporting "stuck", treat it as a strong signal to run the network check in step 3.
  • Stuck child process — a hung git, node, or shell subprocess can freeze the parent. Check pgrep -P <pid> (then ps -p for state — see step 3) for each session.

Argument validation

If the user gave an argument, treat it as a PID only if it consists entirely of digits 0-9. Anything else — letters, whitespace, punctuation — fails the check, in which case treat it as a free-text symptom description (guidance for the report only, never substituted into shell commands). The strict digit-only whitelist is safer than enumerating shell metacharacters.

Investigation steps

Preamble — resolve the runtime base directory. Required for both paths below (sidecar enumeration in step 1, debug log lookup in step 3, and the PID fast path). The base directory is taken from (in priority order): QWEN_RUNTIME_DIR env var, the advanced.runtimeOutputDir setting, QWEN_HOME env var, and finally ~/.qwen.

RUNTIME_DIR="${QWEN_RUNTIME_DIR:-}"
[ -z "$RUNTIME_DIR" ] && command -v jq >/dev/null && RUNTIME_DIR=$(jq -r '.advanced.runtimeOutputDir // empty' "${QWEN_HOME:-$HOME/.qwen}/settings.json" 2>/dev/null)
# `advanced.runtimeOutputDir` may be `~/...` or relative; mirror Storage.resolvePath() before using in globs
[ -n "$RUNTIME_DIR" ] && RUNTIME_DIR="${RUNTIME_DIR/#\~/$HOME}"
[ -n "$RUNTIME_DIR" ] && case "$RUNTIME_DIR" in /*) ;; *) RUNTIME_DIR="$(cd "$RUNTIME_DIR" 2>/dev/null && pwd)" || RUNTIME_DIR="" ;; esac
RUNTIME_DIR="${RUNTIME_DIR:-${QWEN_HOME:-$HOME/.qwen}}"

(If jq isn't installed, the settings layer is silently skipped — the env-var / default fallback covers the common case.)

Fast path for targeted diagnosis — if a digit-only PID argument was given, skip step 1 enumeration. Validate that the PID is a live current-user Qwen Code process before dumping any details:

kill -0 <pid> 2>/dev/null || { echo "PID <pid> is dead, or owned by another user"; exit 0; }
ps -p <pid> -o command= -ww 2>/dev/null | grep -qE '((^|/)qwen-code[^ /]*/[^ ]*\.(js|ts|mjs|cjs)( |$)|/qwen( |$))' || { echo "PID <pid> is yours but is not a Qwen Code process — refusing to dump details"; exit 0; }

If either guard prints, stop the diagnostic and surface the message verbatim. Otherwise, gather stats and the sidecar mapping, then jump to step 3:

ps -p <pid> -o pid=,pcpu=,rss=,etime=,state=,comm=,command= -ww
grep -El '"pid"[[:space:]]*:[[:space:]]*<pid>\b' "$RUNTIME_DIR"/projects/*/chats/*.runtime.json 2>/dev/null

Note: as in step 2, the command= column may include credentials passed as CLI args (e.g., --openai-api-key=sk-…). Redact such values to *** before quoting them in the report.

-E is required so \b is interpreted as word boundary (BSD grep without -E treats \b as a backspace character, silently returning nothing on macOS). The -l flag returns the matching sidecar file path; the basename (stripped of .runtime.json) is the session ID for step 3's debug log read. If multiple sidecars match (rare — happens only after PID reuse leaves a stale file), prefer the most recently modified one: ls -t <matches> | head -n 1.

Otherwise (no arg, or symptom-only arg), run the general path below:

  1. Enumerate live sessions via the runtime sidecar (preferred, reliable):

    Qwen Code writes a runtime.json sidecar for each interactive session at "$RUNTIME_DIR"/projects/<sanitized-cwd>/chats/<sessionId>.runtime.json. Each file contains {schema_version, pid, session_id, work_dir, hostname, started_at, qwen_version} — the authoritative source of (pid, session_id, work_dir) mappings.

    Filter to live (pid, sidecar-path) pairs in one shot. Use Node (guaranteed available — qwen-code requires it) instead of jq (often missing on default macOS / minimal Linux) so this path doesn't silently degrade:

    node -e 'const fs=require("fs"); for (const f of process.argv.slice(1)) { try { const p=JSON.parse(fs.readFileSync(f,"utf8")).pid; if (p) { try { process.kill(p,0); console.log(p+" "+f); } catch {} } } catch {} }' "$RUNTIME_DIR"/projects/*/chats/*.runtime.json 2>/dev/null

    PID reuse is rare but possible — when you cross-reference with ps in step 2, skip pairs whose live PID's command line no longer looks like a Qwen Code process.

    If the command emits nothing (no sidecars, or no live PIDs), fall through to step 2 — ps is the working fallback.

  2. List Qwen Code processes via ps (macOS/Linux) — used to enrich each live session with CPU/RSS/state/uptime, and to catch sessions that may have started before the sidecar feature existed:

    ps -xo pid=,pcpu=,rss=,etime=,state=,comm=,command= -u "$(id -u)" -ww | grep -E '((^|/)qwen-code[^ /]*/[^ ]*\.(js|ts|mjs|cjs)( |$)|/qwen( |$))' | grep -v grep

    -u "$(id -u)" restricts the scan to the current user — on shared hosts this avoids exposing other users' Qwen process paths/arguments into the chat. -ww disables column truncation so long "qwen" paths aren't cut off. The comm column will be node or bun, not qwen; filter to rows where the command column contains a qwen path (e.g., qwen-code/dist/cli.js, or a bin symlink ending in /qwen). Cross-reference with the PIDs from step 1.

    Note: ps reports rss in kilobytes on both macOS and Linux. To report in MB, divide by 1024; to report in GB, divide by 1048576. The 4GB threshold is 4194304 KB — compare the raw rss value against that, or compare the GB value against 4. Do not divide once and then compare against 4; that would flag every process >4MB as "very high RSS".

    Note: full command lines may contain credentials passed as CLI args (e.g., --openai-api-key=sk-…). Redact such values to *** before quoting them in the report.

  3. For anything suspicious, gather more context. If the process state alone explains the problem (T = accidentally stopped, Z = parent not reaping), skip directly to the report — child / log / stack inspection adds nothing. Otherwise:

    • Child processes (with state, so a hung git / node shows up): CHILDREN=$(pgrep -P <pid> | tr '\n' ',' | sed 's/,$//'); [ -n "$CHILDREN" ] && ps -p "$CHILDREN" -o pid=,ppid=,pcpu=,state=,etime=,command= -ww. Single ps call (avoids forking one per child) and -ww so long child command lines aren't truncated.
    • If high CPU: sample again after 1-2s to confirm it's sustained
    • Network hang — if CPU is low and state is S despite the user reporting "stuck", the most likely cause is a hung HTTPS request to the model API. macOS: lsof -nP -i -p <pid> 2>/dev/null | head -20 (the -nP flags skip reverse-DNS and port lookups, which can themselves hang). If lsof itself feels slow, prefix with timeout 10 (or gtimeout 10 on macOS with Homebrew coreutils). Linux: ss -tnp 2>/dev/null | grep "pid=<pid>,". Note that ss -tnp's -p requires root or CAP_NET_ADMIN — without it, the PID column shows - and the grep returns empty. If you see no matches but ss -t 2>/dev/null does show ESTABLISHED sockets, fall back to lsof -nP -i -p <pid> rather than reporting "no connections". A long-lived ESTABLISHED connection to a model host (dashscope, openai, anthropic, etc.) with no recent traffic is the smoking gun.
    • Debug log — start with "$RUNTIME_DIR"/debug/latest (symlink to the most recent session); if it matches the suspicious PID's session, that's usually the right one. Otherwise infer the session ID from the sidecar and read "$RUNTIME_DIR"/debug/<session-id>.txt. Bound the read with tail -n 200 <path> — debug logs can be GB-sized. The last few hundred lines typically show what the session was doing before hanging. Debug logs may contain prompts, file contents, or tokens from other sessions — paste only lines relevant to the hang, and never quote secrets/API keys you happen to see.
  4. Consider a stack dump for a truly frozen process (advanced, optional):

    • macOS: sample <pid> 3 gives a 3-second native stack sample. If sample itself seems to hang (the target's Mach task port may be wedged on a kernel-level freeze), wrap it: timeout 15 sample <pid> 3 (or gtimeout 15 ... on Homebrew coreutils). Stack frames may include function arguments containing API keys or tokens held in memory — redact such values to *** before including the dump in the report.
    • Linux: cat /proc/<pid>/stack for kernel stack (read-only, no ptrace permissions needed). Avoid strace -p for this purpose: it requires CAP_SYS_PTRACE (often denied under kernel.yama.ptrace_scope=1), and strace -c blocks until the target exits — it would hang on the very kind of stuck process you are diagnosing.
    • This is big — only grab it if the process is clearly hung and you want to know why

Report

Present a structured diagnostic report directly to the user with these sections:

For each stuck/slow session found:

  • PID, CPU%, RSS (in MB), process state, uptime, full command line
  • Child processes and their states
  • Your diagnosis of what's likely wrong
  • Relevant debug log tail if you captured it
  • Stack dump output if you captured it
  • Suggested next step for the user to decide (e.g., "user may consider kill <pid> if the session is unresponsive", "likely waiting on I/O — check disk", "accidentally stopped — user can resume with kill -CONT <pid>"). Do not execute these actions yourself — present them as options for the user.

If every session looks healthy, tell the user directly — no diagnostic dump needed. Mention how many sessions you checked and that none showed signs of being stuck.

If no sessions are found at all (zero sidecars and zero matching ps rows), say so explicitly: which RUNTIME_DIR you searched and that ps returned no qwen-related processes for the current user. Suggest the session may have already exited.

Notes

  • Don't kill or signal any processes — this is diagnostic only.
  • If the user gave an argument (e.g., a specific PID or symptom), focus there first.

Frequently asked questions

What does the Stuck AI skill do?

Diagnose frozen, stuck, or slow Qwen Code sessions on this machine. Scans for problematic processes, high CPU/memory usage, hung subprocesses, and debug logs. Use /stuck or /stuck <PID> to focus on a specific process.

Why use Stuck on TypingMind?

Because you install it once and use it with any model. Stuck is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Stuck in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/stuck. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Stuck?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Stuck?

As many as you like. As long as a model supports skills, you can use Stuck with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Stuck AI skill free?

Yes. It is published on GitHub by QwenLM under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇