@rubytech/create-maxy-code 0.1.246 → 0.1.247

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.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  3. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  4. package/payload/platform/plugins/admin/hooks/__tests__/pin-identity-gate.test.sh +96 -0
  5. package/payload/platform/plugins/admin/hooks/pin-identity-gate.sh +136 -0
  6. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts +2 -0
  7. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts.map +1 -0
  8. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js +34 -0
  9. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js.map +1 -0
  10. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts +2 -0
  11. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts.map +1 -0
  12. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js +50 -0
  13. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js.map +1 -0
  14. package/payload/platform/plugins/admin/mcp/dist/index.js +8 -0
  15. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts +6 -0
  17. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts.map +1 -0
  18. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js +32 -0
  19. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js.map +1 -0
  20. package/payload/platform/plugins/admin/skills/investigate/SKILL.md +34 -17
  21. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +9 -4
  22. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +5 -0
  23. package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +5 -0
  24. package/payload/platform/plugins/admin/skills/update-knowledge/SKILL.md +5 -0
  25. package/payload/platform/plugins/docs/references/admin-identity-gate.md +81 -0
  26. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +1 -0
  27. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +5 -0
  28. package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +5 -0
  29. package/payload/platform/scripts/check-skill-frontmatter.mjs +139 -0
  30. package/payload/platform/scripts/setup-account.sh +5 -0
  31. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts +39 -0
  32. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts.map +1 -0
  33. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js +133 -0
  34. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js.map +1 -0
  35. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  36. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  37. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  38. package/payload/platform/services/claude-session-manager/dist/index.js +17 -0
  39. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  40. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  41. package/payload/server/server.js +260 -152
@@ -0,0 +1,32 @@
1
+ // Task 619 — admin-identity-authenticate tool.
2
+ //
3
+ // The agent-callable PIN-submit surface for native-CC admin sessions. The
4
+ // PreToolUse identity gate validates the PIN, writes per-session auth state, and
5
+ // only then allows this tool to run; the tool itself re-asks the same loopback
6
+ // endpoint solely to fetch and return the authenticated operator's profile to
7
+ // the agent. It never touches users.json or Neo4j directly — the endpoint is the
8
+ // single resolution surface, so `loadUserProfile` is not duplicated here.
9
+ export async function runAuthenticate(pin, deps = {}) {
10
+ const f = deps.fetchImpl ?? fetch;
11
+ const port = deps.uiPort ?? process.env.MAXY_UI_INTERNAL_PORT ?? "";
12
+ const url = `http://127.0.0.1:${port}/api/admin/identity/validate`;
13
+ let data;
14
+ try {
15
+ const r = await f(url, {
16
+ method: "POST",
17
+ headers: { "content-type": "application/json" },
18
+ body: JSON.stringify({ pin }),
19
+ });
20
+ data = (await r.json());
21
+ }
22
+ catch {
23
+ return "Identity service unavailable; could not load your profile.";
24
+ }
25
+ if (data.kind !== "match")
26
+ return "Invalid PIN.";
27
+ const name = data.userName ?? "operator";
28
+ const profile = data.aboutOwner?.ok ? (data.aboutOwner.body ?? "") : "";
29
+ console.error(`[admin-identity] op=about-owner-loaded userId=${(data.userId ?? "").slice(0, 8)}`);
30
+ return `Authenticated as ${name}.\n\n${profile}`.trim();
31
+ }
32
+ //# sourceMappingURL=admin-identity-authenticate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-identity-authenticate.js","sourceRoot":"","sources":["../../src/tools/admin-identity-authenticate.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,0EAA0E;AAC1E,iFAAiF;AACjF,+EAA+E;AAC/E,8EAA8E;AAC9E,iFAAiF;AACjF,0EAA0E;AAc1E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,OAAyB,EAAE;IAC5E,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAA;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAA;IACnE,MAAM,GAAG,GAAG,oBAAoB,IAAI,8BAA8B,CAAA;IAElE,IAAI,IAAsB,CAAA;IAC1B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,EAAE;YACrB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;SAC9B,CAAC,CAAA;QACF,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAqB,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,4DAA4D,CAAC;IACtE,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,cAAc,CAAC;IAEjD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,UAAU,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClG,OAAO,oBAAoB,IAAI,QAAQ,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AAC1D,CAAC"}
@@ -41,6 +41,40 @@ deliverable is a task entry, written through the resolved task surface.
41
41
 
42
42
  ---
43
43
 
44
+ ## Step 0: Evidence First — prerequisites before any reasoning
45
+
46
+ **Do this before resolving the task surface, searching prior work,
47
+ reading code, or forming any hypothesis.** The failure mode this guards
48
+ against is forming a hypothesis from the conversation context — the
49
+ error text the user pasted, what you assume the code does — and then
50
+ treating evidence retrieval as redundant. By the time you have read the
51
+ rest of this skill you have already started doing it. Stop.
52
+
53
+ First action in this skill: create a TodoWrite list of the
54
+ prerequisites below. Work through them in order. Do not read code, do
55
+ not search prior tasks, do not propose a cause while any prerequisite
56
+ is still open.
57
+
58
+ 1. **Retrieve the requested evidence.** When the user provides a
59
+ specific artifact — a session key, log reference, error excerpt, or
60
+ pointer to a particular event — that artifact is the primary
61
+ evidence. Retrieve it with the canonical log-retrieval tool for the
62
+ surface (e.g. `logs-read` for Pi sessions on deployed maxy). If a
63
+ retrieval hook injects an exact command, execute it as given; do not
64
+ improvise.
65
+
66
+ This todo may not be marked complete until the **actual retrieved
67
+ output appears in the transcript** — the real log lines, not a
68
+ summary, not a paraphrase, not "the logs confirm X." If you cannot
69
+ produce the output, the todo stays open and that itself is the
70
+ finding: an observability or retrieval gap (see Phase 1 step 6).
71
+
72
+ 2. **If no specific artifact was provided**, mark item 1 N/A and
73
+ proceed to Phase 1 — but state explicitly that no evidence artifact
74
+ was supplied, so the investigation starts from code-path tracing.
75
+
76
+ ---
77
+
44
78
  ## Task surface (where tasks live in this environment)
45
79
 
46
80
  Before searching prior work or writing the deliverable, resolve where
@@ -64,23 +98,6 @@ Throughout this skill, "search prior tasks", "cite prior work", and
64
98
  "write a task" mean: use the resolved surface. Do not invent a new
65
99
  location.
66
100
 
67
- ---
68
-
69
- ## Evidence First
70
-
71
- When the user provides a specific artifact — a session key, log
72
- reference, error excerpt, or pointer to a particular event — that
73
- artifact is the primary evidence. Retrieve it before prior-task
74
- searches, code reading, or hypothesis formation.
75
-
76
- Use the canonical log-retrieval tool for the surface in question (e.g.
77
- `logs-read` for Pi sessions on deployed maxy). If a retrieval hook
78
- injects an exact command, execute it as given; do not improvise.
79
-
80
- If no specific artifact was provided, proceed directly to Phase 1.
81
-
82
- ---
83
-
84
101
  ## Phase 1: Root Cause Investigation
85
102
 
86
103
  1. **Check prior work.** Search the task surface (graph or `.tasks/`)
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: public-agent-manager
3
+ description: "Create, edit, clone, list, preview, and delete public agents, each a self-contained directory with its own identity, personality, knowledge, and model. Use when asked to manage, configure, or set up public agents."
4
+ ---
5
+
1
6
  # Public Agent Manager
2
7
 
3
8
  Create, edit, clone, list, preview, and delete public agents. Each public agent is a self-contained directory under `agents/` with its own identity, personality, knowledge, and model.
@@ -10,7 +15,7 @@ Each agent lives at `{accountDir}/agents/{slug}/`:
10
15
  |------|---------|----------|
11
16
  | `config.json` | Model, display name, status, image identity, knowledge keywords | Yes |
12
17
  | `IDENTITY.md` | Boundaries, behavioural rules — copied verbatim from the Rubytech template | Yes |
13
- | `SOUL.md` | Personality and role — tone, voice, warmth, greeting; plus the agent's job: give basic public info and capture the visitor's contact details for human follow-up (the chat equivalent of the landing-page contact form) | Yes |
18
+ | `SOUL.md` | Personality and the operator-defined role — tone, voice, warmth, greeting, and the role the operator gives the agent at creation | Yes |
14
19
  | `KNOWLEDGE.md` | The business's public-facing facts at landing-page detail — pricing, FAQs, services | Yes |
15
20
  | `KNOWLEDGE-SUMMARY.md` | Auto-generated summary when KNOWLEDGE.md exceeds context budget | No |
16
21
  | `assets/` | Per-agent images (logo, avatar, icon) | No |
@@ -82,10 +87,10 @@ Present as a `select` field within the agent configuration `form` during creatio
82
87
  SOUL.md is personality and role. When the user provides content for SOUL.md, route content that belongs elsewhere to the correct file:
83
88
 
84
89
  - **Facts, pricing, services, FAQs** → KNOWLEDGE.md
85
- - **Engagement and qualification — how the agent draws out what the visitor needs and captures their contact details for human follow-up** → SOUL.md (this is the agent's role; the public agent is toolless, so there are no plugins to embed)
90
+ - **Personality and the operator-defined role — how the agent sounds and what the operator has tasked it to do** → SOUL.md (the role is whatever the operator specifies at creation; the public agent is toolless, so there are no plugins to embed)
86
91
  - **Boundaries and procedural rules** → IDENTITY.md (Rubytech-controlled and copied verbatim — the agent's hard limits live there, not in SOUL)
87
92
 
88
- SOUL.md answers "what does this agent feel like to talk to, and what is it here to do?" — tone, warmth, formality, greeting, and the role of giving basic public info and capturing contact details. SOUL.md must not restate constraints from IDENTITY.md. When user-provided content could belong to either file, apply this test: "does this restrict what the agent does?" → IDENTITY.md. "Does this describe how the agent sounds?" → SOUL.md. If content does both (e.g. "always respond politely"), route to SOUL — the constraint is a consequence of tone, not an operational boundary. Present SOUL.md via `document-editor` for the user to review and approve. Follow the document-editor encoding constraint in IDENTITY.md when generating content.
93
+ SOUL.md answers "what does this agent feel like to talk to, and what is it here to do?" — tone, warmth, formality, greeting, and the operator-defined role the agent was created to fill. SOUL.md must not restate constraints from IDENTITY.md. When user-provided content could belong to either file, apply this test: "does this restrict what the agent does?" → IDENTITY.md. "Does this describe how the agent sounds?" → SOUL.md. If content does both (e.g. "always respond politely"), route to SOUL — the constraint is a consequence of tone, not an operational boundary. Present SOUL.md via `document-editor` for the user to review and approve. Follow the document-editor encoding constraint in IDENTITY.md when generating content.
89
94
 
90
95
  ## KNOWLEDGE.md Population
91
96
 
@@ -148,7 +153,7 @@ After creation, no template metadata persists in the agent's files. The resultin
148
153
  2. Ask for the agent's name (becomes the slug) and role description
149
154
  3. **Knowledge discovery** — search the graph with admin permissions (no `agentSlug` — the agent doesn't exist yet) to find available knowledge. Present available content grouped by category for the user to select.
150
155
  4. **Install `IDENTITY.md`** — copy the Rubytech-controlled template verbatim to `agents/{slug}/IDENTITY.md`. It is fixed (the toolless public directive — Task 615) and is never drafted, edited, or presented for review.
151
- 5. Present `SOUL.md` via document-editor with `filePath: "agents/{slug}/SOUL.md"` (draft personality and role from user conversation — tone plus the job of giving basic public info and capturing the visitor's contact details). Write on approval. Must be non-empty; an empty SOUL is refused at spawn.
156
+ 5. Present `SOUL.md` via document-editor with `filePath: "agents/{slug}/SOUL.md"` (draft personality and the operator-defined role from user conversation — tone plus the role the operator describes for the agent). Write on approval. Must be non-empty; an empty SOUL is refused at spawn.
152
157
  6. Present agent configuration via a single `form` component with these fields:
153
158
  - `knowledgeKeywords` (`text`): label "Knowledge keywords (max 5, comma-separated)", placeholder with examples relevant to the agent's role
154
159
  - `model` (`select`): label "Model", options with descriptions:
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: publish-site
3
+ description: "Move an already-extracted static-site tree into the per-account static-publish surface and emit one canonical path slug. Use when asked to publish, host, or put a site online."
4
+ ---
5
+
1
6
  # Publish Site
2
7
 
3
8
  Call `mcp__plugin_admin_admin__publish-site` to move an already-extracted static-site tree into the per-account static-publish surface (`<accountDir>/sites/<slug>/`) and emit one canonical path slug. Pair the slug with `mcp__plugin_admin_admin__public-hostname` to surface the full URL in the same turn.
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: unzip-attachment
3
+ description: "Safely extract a zip archive the admin has uploaded, inventory its contents, and route each entry to the right downstream specialist, refusing path traversal, symlink escape, and decompression bombs. Use when an admin uploads a zip to unzip or extract."
4
+ ---
5
+
1
6
  # Unzip Attachment
2
7
 
3
8
  Safely extract a zip archive the admin has uploaded, inventory its contents, and propose one concrete follow-up per entry class — refusing archives that attempt path traversal, symlink escape, or decompression-bomb pathologies.
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: update-knowledge
3
+ description: "Refresh a public agent's KNOWLEDGE.md from the current graph, scoped to that agent's knowledge configuration. Use when asked to update, refresh, or regenerate a public agent's knowledge."
4
+ ---
5
+
1
6
  # Update Knowledge
2
7
 
3
8
  Refresh a public agent's KNOWLEDGE.md from the current graph, scoped to that agent's knowledge configuration.
@@ -0,0 +1,81 @@
1
+ # Native-CC admin PIN identity gate
2
+
3
+ Every native-CC admin session — the operator chatting through claude.ai/code (web,
4
+ desktop, mobile) over the per-account `claude rc` daemon — must bind a
5
+ PIN-authenticated `userId` before any tool runs. This closes the gap where the
6
+ daemon stamped one boot-time `USER_ID` (from `users.json`) onto every session,
7
+ which is correct for at most one human on a multi-admin install. (Task 619.)
8
+
9
+ The legacy cookie/PIN browser login (`POST /api/admin/session`) is a separate
10
+ surface documented in [`admin-session.md`](admin-session.md); this gate covers the
11
+ native path, which that login never touched.
12
+
13
+ ## How it works
14
+
15
+ Three pieces, plus a standing audit. No cross-process `session_id` plumbing: the
16
+ hook keys state by the `session_id` it reads from stdin; validation and profile
17
+ resolution live behind one loopback endpoint.
18
+
19
+ 1. **Shared validator** — `ui/app/lib/admin-identity/pin-validator.ts`. The single
20
+ PIN primitive (`validatePin` → `match` / `miss` / `unavailable` / `corrupt`),
21
+ shared by the legacy browser login and the gate so the two cannot drift.
22
+
23
+ 2. **Loopback validate endpoint** — `POST /api/admin/identity/validate`
24
+ (`ui/server/routes/admin/identity.ts`). Loopback-only (rejects any request
25
+ carrying `x-forwarded-for`, which only edge-proxied browser requests have).
26
+ Body `{ pin, sessionId? }`. On a match it resolves the operator's `userName`
27
+ and `aboutOwner` via the same helpers the browser path uses
28
+ (`resolveUserIdentity`, `resolveOwnerProfileBlock`) — `loadUserProfile` is never
29
+ duplicated. On a miss with a `sessionId` it fires a best-effort manager
30
+ `POST /<sessionId>/stop` (only the UI process reliably holds the manager port).
31
+
32
+ 3. **PreToolUse gate hook** — `plugins/admin/hooks/pin-identity-gate.sh`, wired in
33
+ `setup-account.sh` as the first universal PreToolUse entry. Runs only for native
34
+ operator sessions (`MAXY_SESSION_ROLE=admin` and empty `MAXY_SPECIALIST` — the
35
+ latter excludes pty-spawned specialist/recorder sessions). Per tool call: if an
36
+ auth-state file exists for the `session_id`, allow; if the tool is
37
+ `admin-identity-authenticate`, post the PIN to the validate endpoint and on a
38
+ match write the state file and allow (on a miss/unavailable, deny + terminate);
39
+ any other tool while unauthenticated is denied with an instruction to
40
+ authenticate first. Enforcement is in the hook, not the agent's choice — a model
41
+ that skips the prompt reaches no other tool.
42
+
43
+ The agent-callable surface is the eager-loaded `admin-identity-authenticate` MCP
44
+ tool (admin plugin); the gate validates and binds before allowing it, and the tool
45
+ returns the authenticated operator's profile to the agent.
46
+
47
+ Auth state: `<accountDir>/state/admin-identity/<session_id>.json` (`{userId, ts}`).
48
+ One file per authenticated conversation; bound for the session lifetime, not
49
+ re-prompted per message. Task-tool specialist subagents share the operator's
50
+ `session_id`, so they pass once the operator is authenticated.
51
+
52
+ ## Cases
53
+
54
+ - **Zero admins / absent / empty `users.json`** → `pin-unavailable`, the gate
55
+ refuses; never a bound empty `userId`. A real install always has `users.json`
56
+ (the installer/seed writes it), so this is a misconfiguration guard.
57
+ - **One admin** → still prompts; the single matching PIN binds that `userId`.
58
+ - **Many admins** → each PIN binds its own `userId`. The account is fixed (the
59
+ install's own scope); there is no multi-account picker on this path.
60
+ - **Mismatch** → the session is terminated on the first miss (no retry budget).
61
+
62
+ ## Observability
63
+
64
+ One structured `[admin-identity]` line per step, correlated by `sessionId`:
65
+
66
+ ```
67
+ op=loopback-spawn-received → op=pin-prompted → (op=pin-bound source=pin |
68
+ op=pin-miss → op=session-terminated) ; the MCP tool emits op=about-owner-loaded.
69
+ ```
70
+
71
+ **Standing audit** (`services/claude-session-manager/src/admin-identity-audit.ts`,
72
+ every 60s) reconciles, independently of the hook, live native-operator sessions
73
+ (watcher rows with `role === null` — daemon-spawned, no sidecar) that reached real
74
+ tool use against the auth-state files. Any such session with no state file emits
75
+ `[admin-identity] op=audit-orphan sessionId=…
76
+ reason=loopback-session-without-authenticated-userId`. This is the no-event
77
+ detector: a silent gate bypass leaves no per-session log line, but the audit still
78
+ surfaces it.
79
+
80
+ Diagnostic path: `journalctl --user -u <brand>-admin | grep -E '\[admin-identity\]'`,
81
+ then filter by `sessionId` for one session's full lifeline.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: conversation-archive-enrich
3
+ description: "Walk an existing ConversationArchive's chunks and derive high-confidence claims and insights into the graph, one operator-gated proposal per row. Use when asked to enrich, derive insights from, or mine an existing conversation archive."
3
4
  ---
4
5
 
5
6
  # Conversation Archive — chunk-anchored insight derivation
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: conversational-memory
3
+ description: "Notice, store, and recall who the owner is and how they work, accumulated from natural conversation rather than forms. Use when recording or retrieving user profile, preferences, and remembered context across sessions."
4
+ ---
5
+
1
6
  # Conversational Memory
2
7
 
3
8
  Memory accumulates from conversation — never from questionnaires, quizzes, or onboarding forms. The owner reveals who they are and how they work through natural interaction. Your role is to notice, store, and recall that knowledge so every session feels continuous rather than starting from scratch.
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: workflow-manager
3
+ description: "Create, test, and diagnose persistent workflows that automate recurring multi-step processes spanning different plugin tools. Use when the user describes a recurring multi-step process to automate."
4
+ ---
5
+
1
6
  # Workflow Manager
2
7
 
3
8
  Create, test, and diagnose persistent workflows that automate recurring multi-step processes.
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+ // Task 621 — bundle-time frontmatter check for every SKILL.md across the
3
+ // plugins trees.
4
+ //
5
+ // Why: Claude Code surfaces a skill in the session's available-skills reminder,
6
+ // and makes it matchable by the description-driven search tools (ToolSearch,
7
+ // skill-find), ONLY if its SKILL.md opens with YAML frontmatter carrying a
8
+ // `name:` and a `description:`. A SKILL.md that opens straight into an `#` H1
9
+ // has no description for the reminder to list and nothing for fuzzy search to
10
+ // match — it stays invisible until an operator finds the directory by hand,
11
+ // while the agent burns turns on blind find/grep. The skill remains invokable
12
+ // by exact name, so the defect is silent: no error, no log line. This module
13
+ // is the standing guard that fails the bundle before such a payload ships, so
14
+ // the next frontmatter-less skill cannot regress discovery the same way.
15
+ //
16
+ // A file passes iff it opens with a `---\n...\n---` frontmatter block whose
17
+ // keys include both `name:` and `description:`. Three failure shapes are
18
+ // reported distinctly: no frontmatter block at all, missing `name:`, missing
19
+ // `description:`.
20
+
21
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
22
+ import { resolve, relative, join, dirname } from "node:path";
23
+ import { pathToFileURL } from "node:url";
24
+
25
+ // Walk `dir` recursively, collecting every file whose basename matches `predicate`.
26
+ function walk(dir, predicate, out = []) {
27
+ if (!existsSync(dir)) return out;
28
+ let entries;
29
+ try {
30
+ entries = readdirSync(dir);
31
+ } catch {
32
+ return out;
33
+ }
34
+ for (const entry of entries) {
35
+ const full = join(dir, entry);
36
+ let st;
37
+ try {
38
+ st = statSync(full);
39
+ } catch {
40
+ continue;
41
+ }
42
+ if (st.isDirectory()) {
43
+ walk(full, predicate, out);
44
+ } else if (predicate(entry, full)) {
45
+ out.push(full);
46
+ }
47
+ }
48
+ return out;
49
+ }
50
+
51
+ // Extract the leading frontmatter block. A block exists only when the very
52
+ // first line is `---` and a closing `---` follows on its own line. The closing
53
+ // fence is anchored to end-of-line (`\r?\n|$`) so a body line that merely
54
+ // starts with `---` (a `---trailing` divider) cannot be mistaken for the
55
+ // close, and a frontmatter-only file ending exactly at `---` still matches.
56
+ // Returns the block body (between the fences) or null when there is no block.
57
+ function frontmatterBody(raw) {
58
+ const m = raw.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
59
+ return m ? m[1] : null;
60
+ }
61
+
62
+ // True if the block contains a `<key>:` line at the start of any line. The key
63
+ // may be followed by a value, a block scalar (`|`/`>`), or nothing.
64
+ function hasKey(block, key) {
65
+ const re = new RegExp(`^${key}:`, "m");
66
+ return re.test(block);
67
+ }
68
+
69
+ function scanFile(filePath, repoRoot) {
70
+ let raw;
71
+ try {
72
+ raw = readFileSync(filePath, "utf-8");
73
+ } catch {
74
+ return null;
75
+ }
76
+ const source = relative(repoRoot, filePath);
77
+ const block = frontmatterBody(raw);
78
+ if (block === null) {
79
+ return { source, reason: "no YAML frontmatter block (file opens without a leading `---`)" };
80
+ }
81
+ const missing = [];
82
+ if (!hasKey(block, "name")) missing.push("name");
83
+ if (!hasKey(block, "description")) missing.push("description");
84
+ if (missing.length > 0) {
85
+ return { source, reason: `frontmatter missing required key(s): ${missing.join(", ")}` };
86
+ }
87
+ return null;
88
+ }
89
+
90
+ export function runSkillFrontmatterCheck({ platformRoot, premiumRoot }) {
91
+ const repoRoot = resolve(platformRoot, "..");
92
+ const sources = [];
93
+
94
+ // platform: every SKILL.md under plugins/
95
+ walk(resolve(platformRoot, "plugins"), (name) => name === "SKILL.md", sources);
96
+
97
+ // premium: every SKILL.md under any bundle (standalone or sub)
98
+ if (premiumRoot && existsSync(premiumRoot)) {
99
+ walk(premiumRoot, (name) => name === "SKILL.md", sources);
100
+ }
101
+
102
+ const violations = [];
103
+ for (const src of sources) {
104
+ const v = scanFile(src, repoRoot);
105
+ if (v) violations.push(v);
106
+ }
107
+
108
+ return {
109
+ ok: violations.length === 0,
110
+ violations,
111
+ scanned: sources.length,
112
+ };
113
+ }
114
+
115
+ // CLI entry — `node check-skill-frontmatter.mjs [platformRoot] [premiumRoot]`.
116
+ // pathToFileURL keeps the self-detect correct across paths with spaces or
117
+ // non-ASCII characters. When loaded via `import` from bundle.js the URLs
118
+ // differ and this block is skipped.
119
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
120
+ const defaultPlatform = resolve(import.meta.dirname, "..");
121
+ const defaultPremium = resolve(import.meta.dirname, "..", "..", "premium-plugins");
122
+ const platformRoot = process.argv[2] ? resolve(process.argv[2]) : defaultPlatform;
123
+ const premiumRoot = process.argv[3] ? resolve(process.argv[3]) : defaultPremium;
124
+ const r = runSkillFrontmatterCheck({ platformRoot, premiumRoot });
125
+ if (!r.ok) {
126
+ for (const v of r.violations) {
127
+ console.error(`[bundle-validator] skill frontmatter missing`);
128
+ console.error(` source: ${v.source}`);
129
+ console.error(` reason: ${v.reason}`);
130
+ }
131
+ console.error(
132
+ `FATAL: ${r.violations.length} SKILL.md file(s) without name/description frontmatter across ${r.scanned} scanned file(s).`,
133
+ );
134
+ process.exit(1);
135
+ }
136
+ console.log(
137
+ `[bundle-validator] skill frontmatter ok scanned=${r.scanned}`,
138
+ );
139
+ }
@@ -75,6 +75,11 @@ cat > "$ACCOUNT_SETTINGS" << SETTINGS_EOF
75
75
  },
76
76
  "hooks": {
77
77
  "PreToolUse": [
78
+ {
79
+ "hooks": [
80
+ { "type": "command", "command": "bash $HOOKS_PATH/pin-identity-gate.sh" }
81
+ ]
82
+ },
78
83
  {
79
84
  "matcher": "Write",
80
85
  "hooks": [
@@ -0,0 +1,39 @@
1
+ import type { FsWatcher } from './fs-watcher.js';
2
+ export type Logger = (line: string) => void;
3
+ export interface AuditSession {
4
+ sessionId: string;
5
+ /** Sidecar role, or null for native rc-daemon operator sessions. */
6
+ role: string | null;
7
+ state: string;
8
+ /** True when the JSONL contains an assistant tool_use whose name is not the
9
+ * auth tool. */
10
+ usedRealTool: boolean;
11
+ }
12
+ export interface AuditOnceDeps {
13
+ sessions: AuditSession[];
14
+ hasState: (sessionId: string) => boolean;
15
+ logger: Logger;
16
+ }
17
+ /** Pure reconciliation: emit one audit-orphan line per live native-operator
18
+ * session that used a real tool but has no auth-state file. */
19
+ export declare function auditOnce(deps: AuditOnceDeps): number;
20
+ export interface IdentityAuditDeps {
21
+ watcher: FsWatcher;
22
+ /** Account dir (the manager's spawnCwd); used to derive JSONL + state paths. */
23
+ spawnCwd: string;
24
+ /** Directory holding per-session auth-state files written by the gate hook. */
25
+ stateDir: string;
26
+ intervalMs: number;
27
+ logger: Logger;
28
+ }
29
+ export interface IdentityAudit {
30
+ start(): void;
31
+ stop(): void;
32
+ /** Run one audit pass synchronously. Exposed for tests. */
33
+ tickOnce(): {
34
+ scanned: number;
35
+ orphans: number;
36
+ };
37
+ }
38
+ export declare function createIdentityAudit(deps: IdentityAuditDeps): IdentityAudit;
39
+ //# sourceMappingURL=admin-identity-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-identity-audit.d.ts","sourceRoot":"","sources":["../src/admin-identity-audit.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAA;AAU5D,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;AAE3C,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb;qBACiB;IACjB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAA;IACxC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;gEACgE;AAChE,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAarD;AAiCD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,SAAS,CAAA;IAClB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAA;IAChB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;IACZ,2DAA2D;IAC3D,QAAQ,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CACjD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa,CA0C1E"}
@@ -0,0 +1,133 @@
1
+ // Standing audit for the native-CC admin PIN identity gate (Task 619).
2
+ //
3
+ // The no-event-failure detector. The gate hook, the validate endpoint, and the
4
+ // auth tool each emit a log line WHEN they run — but a silent bypass (the gate
5
+ // hook not wired into a session's settings, or a regression that strips it)
6
+ // produces no log line at all. This audit reconciles, independently of the hook,
7
+ // the set of live native-operator sessions that reached real tool use against the
8
+ // set that completed PIN auth (one auth-state file per authenticated session_id).
9
+ // Any live native-operator session that used a real tool with no auth-state file
10
+ // is an orphan: the gate did not bind an identity, yet the session acted.
11
+ //
12
+ // Native-operator classification: a live watcher row whose `role` is null. Native
13
+ // `claude rc` operator sessions are spawned by the rc-daemon, not via the
14
+ // manager's /spawn path, so they carry no sidecar and `role` resolves to null.
15
+ // pty-spawned sessions (recorder, specialists, public, WhatsApp/Telegram)
16
+ // normally carry a sidecar with a non-null role and are excluded. The narrow
17
+ // exception is a pty spawn whose sidecar is briefly absent or unreadable
18
+ // (`applySidecarToRow` defaults role to null) AND has already produced a real
19
+ // tool_use — that would be flagged spuriously. This only ever ADDS an orphan
20
+ // signal (false positive), never hides a real bypass, so the audit errs safe.
21
+ //
22
+ // "Real tool" excludes admin-identity-authenticate itself: a session that only
23
+ // ever called the auth tool either matched (state file written) or missed
24
+ // (terminated), so it never reaches this branch.
25
+ import { existsSync, readFileSync, statSync } from 'node:fs';
26
+ import { join } from 'node:path';
27
+ import { jsonlPathForSessionId } from './jsonl-path.js';
28
+ const TAG = '[admin-identity]';
29
+ const AUTH_TOOL = 'admin-identity-authenticate';
30
+ /** Skip JSONLs larger than this in the tool-use scan — a bounded read keeps the
31
+ * per-tick cost predictable. A session this large has unquestionably used real
32
+ * tools, so treat an oversized file as "used a real tool" (conservative: it can
33
+ * only ever ADD an orphan signal, never hide one). */
34
+ const MAX_SCAN_BYTES = 8 * 1024 * 1024;
35
+ /** Pure reconciliation: emit one audit-orphan line per live native-operator
36
+ * session that used a real tool but has no auth-state file. */
37
+ export function auditOnce(deps) {
38
+ let orphans = 0;
39
+ for (const s of deps.sessions) {
40
+ if (s.state !== 'live')
41
+ continue;
42
+ if (s.role !== null)
43
+ continue; // pty-spawned (sidecar present) — not a native operator
44
+ if (!s.usedRealTool)
45
+ continue; // pre-tool-use session — gate has not been exercised yet
46
+ if (deps.hasState(s.sessionId))
47
+ continue; // authenticated — the gate bound an identity
48
+ deps.logger(`${TAG} op=audit-orphan sessionId=${s.sessionId.slice(0, 8)} reason=loopback-session-without-authenticated-userId`);
49
+ orphans++;
50
+ }
51
+ return orphans;
52
+ }
53
+ /** True when the session JSONL contains an assistant tool_use whose name is not
54
+ * the auth tool. Best-effort: unreadable / unparseable lines are skipped. */
55
+ function jsonlUsedRealTool(jsonlPath) {
56
+ if (!existsSync(jsonlPath))
57
+ return false;
58
+ let raw;
59
+ try {
60
+ if (statSync(jsonlPath).size > MAX_SCAN_BYTES)
61
+ return true;
62
+ raw = readFileSync(jsonlPath, 'utf8');
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ for (const line of raw.split('\n')) {
68
+ if (!line.includes('"tool_use"'))
69
+ continue;
70
+ let obj;
71
+ try {
72
+ obj = JSON.parse(line);
73
+ }
74
+ catch {
75
+ continue;
76
+ }
77
+ const content = obj?.message?.content;
78
+ if (!Array.isArray(content))
79
+ continue;
80
+ for (const block of content) {
81
+ const b = block;
82
+ if (b?.type === 'tool_use' && typeof b.name === 'string' && !b.name.endsWith(AUTH_TOOL)) {
83
+ return true;
84
+ }
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ export function createIdentityAudit(deps) {
90
+ let timer = null;
91
+ function tickOnce() {
92
+ const rows = deps.watcher.all();
93
+ const candidates = rows.filter((r) => r.state === 'live' && r.role === null);
94
+ const sessions = candidates.map((r) => ({
95
+ sessionId: r.sessionId,
96
+ role: r.role,
97
+ state: r.state,
98
+ usedRealTool: jsonlUsedRealTool(jsonlPathForSessionId(deps.spawnCwd, r.sessionId)),
99
+ }));
100
+ const orphans = auditOnce({
101
+ sessions,
102
+ hasState: (sid) => existsSync(join(deps.stateDir, `${sid}.json`)),
103
+ logger: deps.logger,
104
+ });
105
+ return { scanned: candidates.length, orphans };
106
+ }
107
+ return {
108
+ tickOnce,
109
+ start() {
110
+ if (timer)
111
+ return;
112
+ timer = setInterval(() => {
113
+ try {
114
+ tickOnce();
115
+ }
116
+ catch (err) {
117
+ deps.logger(`${TAG} op=audit-tick-failed message=${err instanceof Error ? err.message : String(err)}`);
118
+ }
119
+ }, deps.intervalMs);
120
+ // Don't keep the event loop alive for the audit alone.
121
+ if (typeof timer.unref === 'function')
122
+ timer.unref();
123
+ deps.logger(`${TAG} op=audit-started intervalMs=${deps.intervalMs} stateDir=${deps.stateDir}`);
124
+ },
125
+ stop() {
126
+ if (timer) {
127
+ clearInterval(timer);
128
+ timer = null;
129
+ }
130
+ },
131
+ };
132
+ }
133
+ //# sourceMappingURL=admin-identity-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-identity-audit.js","sourceRoot":"","sources":["../src/admin-identity-audit.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"canonical-tool-names.generated.d.ts","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"AAQA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAiB7C,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAiLtD,CAAA"}
1
+ {"version":3,"file":"canonical-tool-names.generated.d.ts","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"AAQA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAiB7C,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAkLtD,CAAA"}
@@ -33,6 +33,7 @@ export const CANONICAL_MAXY_TOOL_NAMES = [
33
33
  "mcp__plugin_admin_admin__action-pending",
34
34
  "mcp__plugin_admin_admin__action-reject",
35
35
  "mcp__plugin_admin_admin__admin-add",
36
+ "mcp__plugin_admin_admin__admin-identity-authenticate",
36
37
  "mcp__plugin_admin_admin__admin-list",
37
38
  "mcp__plugin_admin_admin__admin-remove",
38
39
  "mcp__plugin_admin_admin__admin-update-pin",