用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Supernor/openclaw-skills --skill project-menu命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | project-menu |
| description | Project lifecycle menu. Start projects, manage them, archive them. Context-aware. Usage: /project-menu |
The project lifecycle entry point. Behavior depends on where Robert invokes it.
Relay executes this skill directly. Do NOT forward to Captain or Scribe for UI rendering.
sessions_spawnIf Relay forwards this skill instead of executing it, no interactive UI will render. Relay is the only agent with Discord component access.
/project-menu — context-aware (detects DM, project channel, or archived channel)
/project-menu <name> — skip topic detection, go straight to project creation for <name>
When invoked with a name argument:
When /project-menu is invoked, detect where Robert is:
cat /home/node/.openclaw/shared-config.json
Get categories.projects and categories.archives IDs.
Use channel-info on the current channel to get its parentId (category).
Read the last 50 messages in the current channel using readMessages.
Analyze the conversation for distinct topics, problems, or project ideas being discussed.
Post a message with buttons for each detected topic (max 4), plus "Something else":
{
"components": {
"text": "Looks like you've been discussing a few things. Start a project?",
"blocks": [{
"type": "actions",
"buttons": [
{ "label": "<Topic 1 - short>", "style": "primary" },
{ "label": "<Topic 2 - short>", "style": "secondary" },
{ "label": "Something else", "style": "secondary" }
]
}]
}
}
If chat history is empty or unclear:
{
"components": {
"text": "What do you want to build?",
"modal": {
"title": "New Project",
"triggerLabel": "Describe it",
"triggerStyle": "primary",
"fields": [
{ "type": "text", "name": "name", "label": "Project name (short, lowercase, hyphens)", "required": true },
{ "type": "text", "name": "goal", "label": "What should it do when it's done?",
When Robert clicks a topic button, open a modal to refine:
{
"components": {
"text": "Good choice. Let me get a few details.",
"modal": {
"title": "Project: <topic>",
"triggerLabel": "Fill in details",
"triggerStyle": "primary",
"fields": [
{ "type": "text", "name": "name", "label": "Channel name (short, lowercase, hyphens)", "required": true },
{ "type": "text", "name": "goal", "label": "What does done look like?"
Using the modal response:
a. Create Discord channel in Projects category:
Use channel-create with:
guildId: from shared-configparentId: categories.projects from shared-configname: from modaltype: 0 (text)b. Initialize tracking files — dispatch to Scribe via sessions_spawn:
{
"tool": "sessions_spawn",
"params": {
"task": "Initialize project tracking for <name>. Goal: <goal from modal>. Create decisions/<name>.md and projects/<name>.md in workspace.",
"agentId": "spec-projects",
"label": "project-menu:init"
}
}
c. Post initial scope card in the new channel:
{
"components": {
"text": "**Project: <name>**\n\n**Goal:** <goal>\n\n**Features:**\n✅ Approved: (none yet)\n🤔 Considering: (none yet)\n🚫 Won't work: (none yet)\n📌 Later: (none yet)\n\nLet me ask a few questions to build the scope.",
"blocks": [{
"type": "actions",
"buttons": [
{ "label": "Looks good, start planning", "style": "success" },
{ "label": "I want to add more context", "style": "secondary" }
]
}]
}
}
d. Ask scoping questions — based on the goal, ask 2-3 targeted questions using buttons or modals. Focus on:
e. Update scope card with answers, categorizing features into the four tiers.
f. Tell Robert where to go: "Head to # to start working. Type /project-menu there anytime to see your options."
Post a select menu with all project management options:
{
"components": {
"text": "**#<channel-name>** — What do you need?",
"blocks": [{
"type": "actions",
"select": {
"type": "string",
"placeholder": "Pick an action",
"options": [
{ "label": "Project Status", "value": "status", "description": "Health check — decisions, tasks, progress" },
{ "label": "Start Planning", "value": "plan-create", "description": "Build a phased execution plan"
sessions_spawn:| Selection | Relay (UI) | Scribe (backend via sessions_spawn) |
|---|---|---|
status | Display formatted result | task: "Get project status for #<channel>", label: "project-menu:status" |
plan-create | Display plan card with approve/modify buttons | task: "Create phased plan for #<channel>. Topic: <topic>", label: "project-menu:plan" |
task-add | Show task modal, collect input | task: "Add task '<title>' assigned to <assignee> in #<channel>", label: "project-menu:task-add" |
decide | Show decision modal, collect input | task: "Log decision in #<channel>: <text>, status: <status>", label: "project-menu:decide" |
decisions | Display formatted decision board | task: "Get decision board for #<channel>", label: "project-menu:decisions" |
context-audit | Display audit results | task: "Run context/token audit for #<channel>", label: "project-menu:context-audit" |
archive | Show archive confirmation buttons | On confirm: task: "Archive project #<channel>", label: "project-menu:archive" |
All sessions_spawn calls use agentId: "spec-projects".
Before rendering, query skill-router.sh list for the current agent roster to populate the assignee dropdown dynamically. Exclude Relay from assignee options (Relay is UI-only, not a task executor).
Fall back to the static list below if the router is unavailable:
{
"components": {
"modal": {
"title": "Add Task",
"triggerLabel": "Add",
"fields": [
{ "type": "text", "name": "title", "label": "What needs to be done?", "required": true },
{ "type": "select", "label": "Assign to", "options": [
{ "label": "Unassigned", "value": "none" },
The static list is a fallback only. Always prefer the dynamic roster.
{
"components": {
"modal": {
"title": "Log Decision",
"triggerLabel": "Log",
"fields": [
{ "type": "text", "name": "text", "label": "What was decided?", "required": true, "style": "paragraph" },
{ "type": "select", "label": "Status", "options": [
{ "label": "Done - decided and implemented",
{
"components": {
"text": "Archive **#<channel-name>**?\n\nThis will:\n- Pin the final decision board\n- Move channel to Archive\n- Set channel read-only\n- Close the session (data kept for reactivation)",
"blocks": [{
"type": "actions",
"buttons": [
{ "label": "Archive - Complete", "style": "success" },
{ "label": "Archive - Partial", "style": "secondary" },
{ "label": "Cancel", "style": "danger" }
]
}]
}
}
On "Archive - Complete" or "Archive - Partial": dispatch to Scribe's archive skill.
After archive completes:
This flow triggers when Robert sends ANY message in a channel that's in the Archives category. Relay should check the channel's category before processing any message.
{
"components": {
"text": "This project is archived. Want to reactivate it?",
"blocks": [{
"type": "actions",
"buttons": [
{ "label": "Yes, reactivate", "style": "success" },
{ "label": "No, just browsing", "style": "secondary" }
]
}]
}
}
a. Move channel back to Projects category:
Use channel-edit to set parentId to categories.projects from shared-config.
b. Remove read-only permission override (re-enable SEND_MESSAGES for @everyone).
c. Reopen/create session for the channel.
d. Read the pinned decision board and any project file to restore context.
e. Post welcome-back message:
{
"components": {
"text": "**#<channel-name>** is active again.\n\nHere's where we left off:\n<summary of last decision board state>\n\nType /plan to see your options.",
"blocks": [{
"type": "actions",
"buttons": [
{ "label": "Continue where I left off", "style": "primary" },
{ "label": "Start fresh", "style": "secondary" }
]
}]
}
}
Reply with a simple text message: "No problem. The history is all here. Type /project-menu if you change your mind."
Do not create a session. Do not process further messages as tasks.
Trigger this when either condition is met:
sessions_spawn result from Scribe contains "all tasks complete" or equivalentWhen triggered, proactively suggest archiving:
{
"components": {
"text": "All tasks complete in **#<channel-name>**! Ready to archive?",
"blocks": [{
"type": "actions",
"buttons": [
{ "label": "Archive - Complete", "style": "success" },
{ "label": "Not yet, more to do", "style": "secondary" }
]
}]
}
}
Relay dispatches backend tasks to Scribe via sessions_spawn. Skip Captain for /project-menu actions to save tokens.
{
"tool": "sessions_spawn",
"params": {
"task": "<descriptive task for Scribe>",
"agentId": "spec-projects",
"label": "project-menu:<action>"
}
}
Relay handles ALL Discord UI rendering. Scribe never renders components.
| Scenario | Relay Action |
|---|---|
shared-config.json missing or unreadable | Inform Robert: "Config file missing — can't detect channel context." Offer button: "Notify Captain" |
| Channel creation fails | Show error with buttons: "Retry" / "Use different name" |
Scribe sessions_spawn fails or times out | Show warning: "Backend task didn't complete." Buttons: "Retry" / "Skip" |
| Robert abandons mid-flow (no response) | Do NOT continue prompting. Start fresh on next /project-menu invocation |
| Permission error (bot lacks Discord permissions) | Tell Robert which permission is needed (e.g., "Bot needs Manage Channels in the Projects category") |
Intent: Responsive [I04]. Purpose: [P-TBD].