smashspace 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -1
- package/dist/smash.mjs +299 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,9 +28,31 @@ The token is delivered to a local loopback port and saved to
|
|
|
28
28
|
`~/.config/smash/credentials.json`. The same credential is used by the MCP
|
|
29
29
|
server below. (Headless? use `smash login --token <PAT>`.)
|
|
30
30
|
|
|
31
|
+
## Onboard a project for agents: `smash init`
|
|
32
|
+
|
|
33
|
+
Run once per repo to scaffold everything agents (Claude Code / Codex) need to
|
|
34
|
+
operate SmashSpace as the task backbone — no manual copying:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
smash init --board-id b_xxxxxxxx
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
It writes (and is safe to re-run — existing config is kept unless `--force`):
|
|
41
|
+
|
|
42
|
+
- `.claude/skills/smash/SKILL.md` + `.codex/skills/smash/SKILL.md` — the
|
|
43
|
+
SmashSpace Operator playbook (CLI/MCP usage + Inbox→Todo→Doing→Review→Done
|
|
44
|
+
workflow + safety rules). Always refreshed to the installed CLI version.
|
|
45
|
+
- `.smashspace.json` — pins the instance + board.
|
|
46
|
+
- `.mcp.json` — wires the `smashspace` MCP server (merged, other servers kept).
|
|
47
|
+
|
|
48
|
+
Flags: `--skills-only` (just the operator skill), `--no-mcp`, `--force`,
|
|
49
|
+
`--board-id <id>`. Then `smash login` once on the machine and the agents are
|
|
50
|
+
ready.
|
|
51
|
+
|
|
31
52
|
## Project config: `.smashspace.json`
|
|
32
53
|
|
|
33
|
-
|
|
54
|
+
`smash init` writes this for you; drop it at your repo root manually if you
|
|
55
|
+
prefer, so `smash` knows which board to operate on:
|
|
34
56
|
|
|
35
57
|
```json
|
|
36
58
|
{
|
package/dist/smash.mjs
CHANGED
|
@@ -3392,10 +3392,10 @@ var {
|
|
|
3392
3392
|
import { spawn } from "node:child_process";
|
|
3393
3393
|
import { createServer } from "node:http";
|
|
3394
3394
|
import { randomBytes } from "node:crypto";
|
|
3395
|
-
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
3395
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
3396
3396
|
import { platform as platform2 } from "node:os";
|
|
3397
3397
|
import { createInterface } from "node:readline/promises";
|
|
3398
|
-
import { relative } from "node:path";
|
|
3398
|
+
import { relative, join as join3, dirname as dirname3 } from "node:path";
|
|
3399
3399
|
import { stdin, stdout } from "node:process";
|
|
3400
3400
|
|
|
3401
3401
|
// ../../shared/board-registry.ts
|
|
@@ -3572,6 +3572,219 @@ function resolveBoardEntry(config, label) {
|
|
|
3572
3572
|
return entry;
|
|
3573
3573
|
}
|
|
3574
3574
|
|
|
3575
|
+
// ../../cli/templates.ts
|
|
3576
|
+
var SMASH_SKILL_MD = `---
|
|
3577
|
+
name: smash
|
|
3578
|
+
description: Operate SmashSpace task boards from the terminal and MCP. Use when the user asks for /smash list, /smash inbox, /smash todo-dig, /smash todo-dev, /smash review, task-board triage, card development, card review, or moving SmashSpace cards between Inbox/Todo/Doing/Review/Done.
|
|
3579
|
+
---
|
|
3580
|
+
|
|
3581
|
+
# SmashSpace Operator
|
|
3582
|
+
|
|
3583
|
+
Use this skill to run repeatable workflows against a SmashSpace task board.
|
|
3584
|
+
|
|
3585
|
+
SmashSpace is a Cloudflare-native Kanban where people work in the UI and AI
|
|
3586
|
+
agents work through the CLI, MCP, and HTTP API on the **same** data.
|
|
3587
|
+
Product: <https://smashspace.app>.
|
|
3588
|
+
|
|
3589
|
+
## How to reach the board
|
|
3590
|
+
|
|
3591
|
+
Operate through the **\`smashspace-mcp\` MCP tools** when available; otherwise fall
|
|
3592
|
+
back to the **\`smash\` CLI**. Do **not** hit the HTTP API with \`curl\` or a raw
|
|
3593
|
+
browser \u2014 the CLI and MCP are the supported surfaces and they resolve auth and
|
|
3594
|
+
the target board for you.
|
|
3595
|
+
|
|
3596
|
+
- CLI install: \`npm i -g smashspace\` (then \`smash --help\`)
|
|
3597
|
+
- MCP server: \`smashspace-mcp\` (exposes \`smash_*\` tools, same token as the CLI)
|
|
3598
|
+
- Sign in once per machine: \`smash login\` (browser approval; token saved to
|
|
3599
|
+
\`~/.config/smash/credentials.json\` and reused by MCP)
|
|
3600
|
+
|
|
3601
|
+
### Which board am I on?
|
|
3602
|
+
|
|
3603
|
+
\`smash\` is **directory-aware**: it walks up from the current directory to the
|
|
3604
|
+
nearest \`.smashspace.json\`. **Always confirm context before acting:**
|
|
3605
|
+
|
|
3606
|
+
\`\`\`bash
|
|
3607
|
+
smash status # config file, instance, board, signed-in account
|
|
3608
|
+
\`\`\`
|
|
3609
|
+
|
|
3610
|
+
Pick a board with \`-b <label>\` (defaults to \`main\`), or bypass config with
|
|
3611
|
+
\`--board-id <id>\`. Canonical lists on a task board: \`Inbox\`, \`Todo\`, \`Doing\`,
|
|
3612
|
+
\`Review\`, \`Done\`.
|
|
3613
|
+
|
|
3614
|
+
## Safety Rules
|
|
3615
|
+
|
|
3616
|
+
- Never move a card to \`Done\` without verification.
|
|
3617
|
+
- This workflow uses \`Done\` \u2014 there is no separate "Complete" state.
|
|
3618
|
+
- Before development, inspect the **full** card: title, description, checklist,
|
|
3619
|
+
labels, comments, images/attachments, artifacts, external refs, assignees,
|
|
3620
|
+
custom data, and \`agent_meta\`.
|
|
3621
|
+
- Before changing a card, leave a comment that explains the plan.
|
|
3622
|
+
- Preserve user changes in the repo. Do not revert unrelated dirty files.
|
|
3623
|
+
- Scope remote writes to the resolved board (\`smash status\` to confirm).
|
|
3624
|
+
- If an operation can affect production data beyond the task board, ask first.
|
|
3625
|
+
|
|
3626
|
+
## Command vocabulary
|
|
3627
|
+
|
|
3628
|
+
MCP-first; the CLI equivalents are shown so the workflow is reproducible.
|
|
3629
|
+
|
|
3630
|
+
\`\`\`bash
|
|
3631
|
+
smash status # resolved config / instance / board / account
|
|
3632
|
+
smash read # show the board (lists + cards)
|
|
3633
|
+
smash scan --list "Todo" # actionable cards as JSON (best for agents)
|
|
3634
|
+
smash create "Fix signup bug" # add a card to the resolved board (--list to target)
|
|
3635
|
+
smash move <cardId> "Review" # move a card to a list by name
|
|
3636
|
+
smash archive <cardId> # archive (soft-delete) a card
|
|
3637
|
+
|
|
3638
|
+
smash card show <cardId> # full card detail before acting
|
|
3639
|
+
smash card update <cardId> --title/--desc ... # edit fields
|
|
3640
|
+
smash comment add <cardId> "plan / progress" # leave a comment
|
|
3641
|
+
smash checklist add <cardId> "step" # checklist items
|
|
3642
|
+
smash agent-meta set <cardId> --data '{...}' # structured agent state (see below)
|
|
3643
|
+
smash artifact create <cardId> --kind markdown --name "Report" --content-md-file ./r.md
|
|
3644
|
+
\`\`\`
|
|
3645
|
+
|
|
3646
|
+
Use \`smash <group> --help\` for the full surface (\`board\`/\`list\`/\`card\`/
|
|
3647
|
+
\`comment\`/\`checklist\`/\`label\`/\`artifact\`/\`attachment\`/\`agent-meta\`/
|
|
3648
|
+
\`external-refs\`/\`assignees\`).
|
|
3649
|
+
|
|
3650
|
+
## List matching
|
|
3651
|
+
|
|
3652
|
+
Use exact canonical names when present. For Todo-like selection, match
|
|
3653
|
+
case-insensitively and semantically: \`Todo\`, \`To Do\`, \`\u3084\u308B\u3053\u3068\`, \`\u3084\u308B\`,
|
|
3654
|
+
\`\u30BF\u30B9\u30AF\`, \`Backlog\`, \`Next\`, \`Ready\`, \`Planned\`. Exclude \`Doing\`, \`Review\`,
|
|
3655
|
+
\`Done\`, and archive-like lists unless the user explicitly asks.
|
|
3656
|
+
|
|
3657
|
+
## \`agent_meta\` convention
|
|
3658
|
+
|
|
3659
|
+
Keep the card's agent state current while you work:
|
|
3660
|
+
|
|
3661
|
+
\`\`\`json
|
|
3662
|
+
{
|
|
3663
|
+
"primary_agent": "claude-code", // or "codex"
|
|
3664
|
+
"status": "working", // working | waiting_review | done
|
|
3665
|
+
"turn": "agent", // agent | human
|
|
3666
|
+
"current_step": "concise phase",
|
|
3667
|
+
"progress": 0.4 // realistic partial value
|
|
3668
|
+
}
|
|
3669
|
+
\`\`\`
|
|
3670
|
+
|
|
3671
|
+
Set \`status: "done"\` only after verification passes.
|
|
3672
|
+
|
|
3673
|
+
## Command: /smash list
|
|
3674
|
+
|
|
3675
|
+
Show the current board state before choosing work. Do **not** mutate.
|
|
3676
|
+
|
|
3677
|
+
1. Read the resolved board (\`smash read\`, or \`smash scan\` for JSON).
|
|
3678
|
+
2. Report board title, board ID, each list (name, ID, card count), and active
|
|
3679
|
+
card titles, kept compact.
|
|
3680
|
+
|
|
3681
|
+
Infer options from wording: \`brief\` (names + counts), \`full\` (all cards incl.
|
|
3682
|
+
Done), \`todo\` / \`review\` / \`inbox\` (that list only), \`ids\` (include IDs).
|
|
3683
|
+
|
|
3684
|
+
## Command: /smash inbox
|
|
3685
|
+
|
|
3686
|
+
Research the board and create actionable improvement cards in \`Inbox\`.
|
|
3687
|
+
|
|
3688
|
+
1. Read the current board to avoid duplicates.
|
|
3689
|
+
2. Generate concrete ideas from multiple perspectives (entrepreneur, UI/UX,
|
|
3690
|
+
engineer, AI specialist).
|
|
3691
|
+
3. For each, create one \`Inbox\` card with a specific title, a detailed
|
|
3692
|
+
description (user value, expected behavior, acceptance criteria), and an
|
|
3693
|
+
optional checklist. Add a short comment naming the perspective and rationale.
|
|
3694
|
+
|
|
3695
|
+
Quality bar: titles must be concrete and implementable; no vague cards like
|
|
3696
|
+
"Improve UI"; avoid duplicating existing cards.
|
|
3697
|
+
|
|
3698
|
+
## Command: /smash todo-dig
|
|
3699
|
+
|
|
3700
|
+
Deeply analyze Todo-like cards and prepare them for implementation.
|
|
3701
|
+
|
|
3702
|
+
1. Find Todo-like lists (see list matching).
|
|
3703
|
+
2. Inspect all card data (\`smash card show <cardId>\`).
|
|
3704
|
+
3. Comment the implementation approach, likely files/modules, risks, unknowns,
|
|
3705
|
+
and validation plan.
|
|
3706
|
+
4. Update \`agent_meta\` (\`status: working\`, \`turn: agent\`, \`progress: ~0.2\`).
|
|
3707
|
+
5. Create/update a checklist with implementation and verification steps.
|
|
3708
|
+
6. Do not move cards unless asked.
|
|
3709
|
+
|
|
3710
|
+
Run cards in parallel only when implementation areas do not overlap.
|
|
3711
|
+
|
|
3712
|
+
## Command: /smash todo-dev
|
|
3713
|
+
|
|
3714
|
+
Implement Todo-like cards end to end.
|
|
3715
|
+
|
|
3716
|
+
1. Find Todo-like lists; inspect the full card.
|
|
3717
|
+
2. Comment the implementation approach **before editing code**.
|
|
3718
|
+
3. Move the card to \`Doing\` (\`smash move <cardId> "Doing"\`).
|
|
3719
|
+
4. Update \`agent_meta\` (\`status: working\`, \`progress\` starting near \`0.1\`).
|
|
3720
|
+
5. Implement the change in the repo.
|
|
3721
|
+
6. Run appropriate checks: \`npm run build\`; unit/API/contract tests when
|
|
3722
|
+
relevant; a dev-browser/UI check for UI changes.
|
|
3723
|
+
7. Update checklist items as completed.
|
|
3724
|
+
8. Add a completion comment with changed files and verification results.
|
|
3725
|
+
9. Move to \`Review\` \u2014 **not** \`Done\` \u2014 unless /smash review has already verified.
|
|
3726
|
+
|
|
3727
|
+
Use subagents only for independent work with disjoint file ownership.
|
|
3728
|
+
|
|
3729
|
+
## Command: /smash review
|
|
3730
|
+
|
|
3731
|
+
Verify \`Review\` cards and move verified work to \`Done\`.
|
|
3732
|
+
|
|
3733
|
+
1. Inspect the card; understand what must be verified.
|
|
3734
|
+
2. Choose verification: UI change \u2192 run the app/dev-browser; logic/API change \u2192
|
|
3735
|
+
unit/integration/API tests; broad change \u2192 \`npm run build\` + targeted tests.
|
|
3736
|
+
3. Inspect screenshots or test output.
|
|
3737
|
+
4. On pass: comment the verification command/result, move \`Review\` \u2192 \`Done\`, set
|
|
3738
|
+
\`agent_meta.status: done\`.
|
|
3739
|
+
5. On fail: keep in \`Review\` or move back to \`Doing\`, comment the failure and the
|
|
3740
|
+
next action.
|
|
3741
|
+
|
|
3742
|
+
## Fallbacks
|
|
3743
|
+
|
|
3744
|
+
- Prefer \`smashspace-mcp\` MCP tools. If unavailable, use the \`smash\` CLI.
|
|
3745
|
+
- If the CLI is not installed: \`npm i -g smashspace\`, then \`smash login\`.
|
|
3746
|
+
- If a board returns "Sign-in required": run \`smash login\`, or set \`SMASH_TOKEN\`
|
|
3747
|
+
for a headless/CI agent.
|
|
3748
|
+
|
|
3749
|
+
## Required Final Report
|
|
3750
|
+
|
|
3751
|
+
At the end of any /smash workflow, report:
|
|
3752
|
+
|
|
3753
|
+
- board and list(s) touched
|
|
3754
|
+
- cards created, moved, or updated
|
|
3755
|
+
- comments / checklists / custom data / \`agent_meta\` changes
|
|
3756
|
+
- verification commands and results
|
|
3757
|
+
- any cards left blocked or unverified
|
|
3758
|
+
`;
|
|
3759
|
+
var OPENAI_AGENT_YAML = `interface:
|
|
3760
|
+
display_name: "SmashSpace Operator"
|
|
3761
|
+
short_description: "Run SmashSpace task-board workflows."
|
|
3762
|
+
default_prompt: "Use $smash to run /smash review on the resolved board."
|
|
3763
|
+
`;
|
|
3764
|
+
function smashspaceJson(baseUrl, boardId) {
|
|
3765
|
+
return `${JSON.stringify(
|
|
3766
|
+
{
|
|
3767
|
+
base_url: baseUrl,
|
|
3768
|
+
default: "main",
|
|
3769
|
+
spaces: {
|
|
3770
|
+
main: { space_id: boardId, comment: "Team task board" }
|
|
3771
|
+
}
|
|
3772
|
+
},
|
|
3773
|
+
null,
|
|
3774
|
+
2
|
|
3775
|
+
)}
|
|
3776
|
+
`;
|
|
3777
|
+
}
|
|
3778
|
+
function mcpServerEntry(baseUrl, boardId) {
|
|
3779
|
+
return {
|
|
3780
|
+
command: "smashspace-mcp",
|
|
3781
|
+
env: {
|
|
3782
|
+
SMASH_BASE_URL: baseUrl,
|
|
3783
|
+
SMASH_SPACES: `${baseUrl.replace(/\/+$/, "")}/space/${boardId}`
|
|
3784
|
+
}
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3575
3788
|
// ../../cli/index.ts
|
|
3576
3789
|
var DEFAULT_BASE_URL = process.env.SMASH_BASE_URL ?? "https://smashspace.app";
|
|
3577
3790
|
var program2 = new Command();
|
|
@@ -4479,6 +4692,90 @@ program2.command("status").aliases(["context", "where"]).description(
|
|
|
4479
4692
|
}
|
|
4480
4693
|
show(void 0, lines);
|
|
4481
4694
|
});
|
|
4695
|
+
program2.command("init").description(
|
|
4696
|
+
"Scaffold this project for agents: operator skill + .smashspace.json + .mcp.json"
|
|
4697
|
+
).option("--board-id <id>", "Board/space id to pin in .smashspace.json / .mcp.json").option("--skills-only", "Only (re)install the operator skill files").option("--no-mcp", "Skip writing .mcp.json").option("-f, --force", "Overwrite .smashspace.json / .mcp.json if they exist").action(
|
|
4698
|
+
async (opts) => {
|
|
4699
|
+
const cwd = process.cwd();
|
|
4700
|
+
const actions = [];
|
|
4701
|
+
const abs = (rel) => join3(cwd, rel);
|
|
4702
|
+
const put = async (rel, content) => {
|
|
4703
|
+
const p = abs(rel);
|
|
4704
|
+
await mkdir2(dirname3(p), { recursive: true });
|
|
4705
|
+
await writeFile2(p, content, "utf8");
|
|
4706
|
+
};
|
|
4707
|
+
const exists = async (rel) => {
|
|
4708
|
+
try {
|
|
4709
|
+
await readFile2(abs(rel));
|
|
4710
|
+
return true;
|
|
4711
|
+
} catch {
|
|
4712
|
+
return false;
|
|
4713
|
+
}
|
|
4714
|
+
};
|
|
4715
|
+
await put(".claude/skills/smash/SKILL.md", SMASH_SKILL_MD);
|
|
4716
|
+
await put(".codex/skills/smash/SKILL.md", SMASH_SKILL_MD);
|
|
4717
|
+
await put(".codex/skills/smash/agents/openai.yaml", OPENAI_AGENT_YAML);
|
|
4718
|
+
actions.push(
|
|
4719
|
+
"\u2713 operator skill \u2192 .claude/skills/smash/ + .codex/skills/smash/"
|
|
4720
|
+
);
|
|
4721
|
+
const baseUrl = resolveBaseUrl();
|
|
4722
|
+
const boardId = opts.boardId ?? "b_xxxxxxxx";
|
|
4723
|
+
const placeholder = boardId === "b_xxxxxxxx";
|
|
4724
|
+
if (!opts.skillsOnly) {
|
|
4725
|
+
if (await exists(CONFIG_FILENAME) && !opts.force) {
|
|
4726
|
+
actions.push(`\u2022 ${CONFIG_FILENAME} exists \u2014 kept (use --force)`);
|
|
4727
|
+
} else {
|
|
4728
|
+
await put(CONFIG_FILENAME, smashspaceJson(baseUrl, boardId));
|
|
4729
|
+
actions.push(
|
|
4730
|
+
`\u2713 ${CONFIG_FILENAME}${placeholder ? " (placeholder space_id \u2014 edit it)" : ` (${boardId})`}`
|
|
4731
|
+
);
|
|
4732
|
+
}
|
|
4733
|
+
if (opts.mcp !== false) {
|
|
4734
|
+
let mcp = { mcpServers: {} };
|
|
4735
|
+
let parseFailed = false;
|
|
4736
|
+
if (await exists(".mcp.json")) {
|
|
4737
|
+
try {
|
|
4738
|
+
const parsed = JSON.parse(await readFile2(abs(".mcp.json"), "utf8"));
|
|
4739
|
+
if (parsed && typeof parsed === "object")
|
|
4740
|
+
mcp = parsed;
|
|
4741
|
+
} catch {
|
|
4742
|
+
parseFailed = true;
|
|
4743
|
+
}
|
|
4744
|
+
if (!mcp.mcpServers || typeof mcp.mcpServers !== "object") {
|
|
4745
|
+
mcp.mcpServers = {};
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
if (parseFailed && !opts.force) {
|
|
4749
|
+
actions.push(
|
|
4750
|
+
"\u26A0 .mcp.json exists but is not valid JSON \u2014 left untouched; add the 'smashspace' server by hand or re-run with --force"
|
|
4751
|
+
);
|
|
4752
|
+
} else {
|
|
4753
|
+
const servers = mcp.mcpServers;
|
|
4754
|
+
if (servers.smashspace && !opts.force) {
|
|
4755
|
+
actions.push("\u2022 .mcp.json already wires 'smashspace' \u2014 kept (use --force)");
|
|
4756
|
+
} else {
|
|
4757
|
+
servers.smashspace = mcpServerEntry(baseUrl, boardId);
|
|
4758
|
+
await put(".mcp.json", `${JSON.stringify(mcp, null, 2)}
|
|
4759
|
+
`);
|
|
4760
|
+
actions.push("\u2713 .mcp.json \u2014 smashspace MCP server");
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
}
|
|
4765
|
+
const next = ["", "Next:", " 1. smash login # sign in (browser approval)"];
|
|
4766
|
+
if (!opts.skillsOnly && placeholder) {
|
|
4767
|
+
next.push(" 2. edit .smashspace.json # set your real space_id (`smash board list` to find it)");
|
|
4768
|
+
}
|
|
4769
|
+
next.push(
|
|
4770
|
+
" \u2192 Claude Code / Codex pick up .claude|.codex/skills/smash automatically"
|
|
4771
|
+
);
|
|
4772
|
+
if (getOpts().json) {
|
|
4773
|
+
show({ cwd, actions, baseUrl, boardId: placeholder ? null : boardId });
|
|
4774
|
+
return;
|
|
4775
|
+
}
|
|
4776
|
+
show(void 0, [...actions, ...next]);
|
|
4777
|
+
}
|
|
4778
|
+
);
|
|
4482
4779
|
program2.command("create <title>").description(`Create a card on the resolved board (${CONFIG_FILENAME}, default "main")`).option("-b, --board <label>", "board label from config").option("--board-id <id>", "explicit board id (bypass config)").option("-l, --list <name>", "target list name (default: first list)").option("-d, --desc <text>", "card description (Markdown)").action(async (title, opts) => {
|
|
4483
4780
|
const ctx = resolveContext(opts);
|
|
4484
4781
|
const detail = await fetchBoard(ctx.boardId, ctx.baseUrl);
|