diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a03488..2036dcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **`memory` listing type**: `positronick memory search|list|show|install` — platform LISTING_TYPES +- **Listing categories**: Memory and Observability on admin validation - **`positronick research`**: the agent-facing "what's new" feed over positronick.com's published blog posts, mirrored GitHub releases, and mirrored news links — newest first, so agents avoid stale knowledge. Filter with `--kind` (article/release/link), `--category`, `--tag`, or a free-text query, and poll just the delta with `--since `; the printed `latest` timestamp is the value to pass back next time. Read-only and unauthenticated, like the soul/listing reads. Backed by the public `GET /api/research` endpoint. - **`positronick blog`**: read the positronick.com blog from the terminal — `blog list` (newest first, optional `--kind` article/release/link) and `blog show `, with `--raw` printing the markdown body verbatim and did-you-mean hints (exit 3) on a missing slug. Read-only and unauthenticated, mirroring the soul/listing reads. Backed by the public `GET /api/blog`, `/api/blog/{slug}`, and `/api/blog/{slug}.md` endpoints. - **`positronick feed` (admin)**: manage the blog feed sources (GitHub release / RSS mirroring) the ingestor polls — `feed list`, `feed create --label --feed-url --kind github_release|rss --category ` (`--author`/`--listing` attribution, repeatable `--tag`, `--auto-publish`, `--enabled`), `feed update ` (`--enabled=false` pauses a feed — there is no delete verb), and `feed sync ` (ingest one feed now, surfacing the fetch summary; a fetch/parse failure maps the API's 502 to a clear error). Backed by the `/api/admin/feeds` API. Ingesting every feed on a schedule stays the cron's job — no CLI subcommand carries that admin key. diff --git a/README.md b/README.md index 56921f8..7dafeb1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Positronick CLI -`positronick` discovers and installs agent capabilities — souls, harnesses, CLIs, MCP servers, agents, skills, plugins, and loops — from [positronick.com](https://positronick.com). +`positronick` discovers and installs agent capabilities — souls, harnesses, CLIs, MCP servers, memory, agents, skills, plugins, and loops — from [positronick.com](https://positronick.com). ## Install @@ -38,6 +38,7 @@ yay -S positronick-bin # Arch (AUR) | `positronick harness search\|show\|list\|install` | Agent harnesses | available | | `positronick cli search\|show\|list\|install` | CLI tools | available | | `positronick mcp search\|show\|list\|install` | MCP servers | available | +| `positronick memory search\|show\|list\|install` | Memory and context engines | available | | `positronick agent search\|show\|list\|install` | Agents | available | | `positronick skill search\|show\|list\|install` | Skills | available | | `positronick plugin search\|show\|list\|install` | Plugins | available | diff --git a/internal/api/types.go b/internal/api/types.go index 2ef1775..9e2db7b 100644 --- a/internal/api/types.go +++ b/internal/api/types.go @@ -19,7 +19,14 @@ import ( // ListingTypes are the kinds of official tooling the registry catalogs. // Mirrors LISTING_TYPES in src/lib/types.ts. -var ListingTypes = []string{"harness", "cli", "mcp", "agent", "skill", "plugin", "loop"} +var ListingTypes = []string{"harness", "cli", "mcp", "memory", "agent", "skill", "plugin", "loop"} + +// ListingCategories are the broad subject labels a listing may use. +// Mirrors LISTING_CATEGORIES in src/lib/types.ts. +var ListingCategories = []string{ + "AI/ML", "Memory", "Observability", "DevOps", "Cloud", "Web", + "Data", "Security", "Technical", "Productivity", +} // FeedKinds are the kinds of blog feed source the ingestor mirrors. Mirrors // FEED_KINDS in src/lib/server/feedFields.ts. diff --git a/internal/api/types_test.go b/internal/api/types_test.go index 47750cc..f65c056 100644 --- a/internal/api/types_test.go +++ b/internal/api/types_test.go @@ -167,3 +167,25 @@ func TestNewWireFieldsRoundTrip(t *testing.T) { t.Errorf("SoulCard.ChargeCount = %d, want 5", s.ChargeCount) } } + +// Platform LISTING_TYPES order is a public contract: the CLI auto-registers one +// cobra noun per entry, skill_contract pins the joined list, and MCP type enums +// derive from it. Keep this in lockstep with src/lib/types.ts. +func TestListingTypesMatchPlatformOrder(t *testing.T) { + want := []string{"harness", "cli", "mcp", "memory", "agent", "skill", "plugin", "loop"} + if !reflect.DeepEqual(ListingTypes, want) { + t.Fatalf("ListingTypes = %#v\nwant %#v", ListingTypes, want) + } +} + +// Platform LISTING_CATEGORIES order is a public contract: mockadmin validation +// and any future UI filters must accept exactly these labels. +func TestListingCategoriesMatchPlatformOrder(t *testing.T) { + want := []string{ + "AI/ML", "Memory", "Observability", "DevOps", "Cloud", "Web", + "Data", "Security", "Technical", "Productivity", + } + if !reflect.DeepEqual(ListingCategories, want) { + t.Fatalf("ListingCategories = %#v\nwant %#v", ListingCategories, want) + } +} diff --git a/internal/cli/admin.go b/internal/cli/admin.go index 033fcba..c30e55f 100644 --- a/internal/cli/admin.go +++ b/internal/cli/admin.go @@ -493,8 +493,8 @@ func newListingCmd() *cobra.Command { cmd := markAdmin(&cobra.Command{ Use: "listing", Short: "Create and update registry listings (admin)", - Long: "Write access to registry listings of any type — the public type nouns (harness, " + - "cli, mcp, agent, skill, plugin, loop) stay read-only. Listings are authored by an " + + Long: "Write access to registry listings of any type — the public type nouns (" + + strings.Join(api.ListingTypes, ", ") + ") stay read-only. Listings are authored by an " + "existing profile handle; profiles themselves stay git-curated." + adminNote, }) cmd.AddCommand(newListingCreateCmd(), newListingUpdateCmd()) diff --git a/internal/cli/agentdocs.go b/internal/cli/agentdocs.go index 5fb2970..fdd4549 100644 --- a/internal/cli/agentdocs.go +++ b/internal/cli/agentdocs.go @@ -43,7 +43,7 @@ var exitCodeMeanings = map[string]string{ const agentDocsIntro = "`positronick` is the command-line client for positronick.com. It discovers agent " + "capabilities: souls (installable SOUL.md personality files), a registry of official " + - "tooling (harnesses, CLIs, MCP servers, agents, skills, plugins, loops), and a `research` " + + "tooling (harnesses, CLIs, MCP servers, memory, agents, skills, plugins, loops), and a `research` " + "feed of what's new (articles, releases, links) so agents avoid stale knowledge. It is built to be " + "driven by coding agents — pass `--json` to any command for stable machine-readable JSON on " + "stdout, read progress and errors from stderr, and branch on the exit code. Read commands " + diff --git a/internal/cli/install.go b/internal/cli/install.go index ac633bb..563728d 100644 --- a/internal/cli/install.go +++ b/internal/cli/install.go @@ -27,7 +27,7 @@ func attachInstallCommands(root *cobra.Command) { c.AddCommand(newSoulInstallCmd()) case "loop": c.AddCommand(newLoopInstallCmd()) - case "harness", "cli", "mcp", "agent", "skill", "plugin": + case "harness", "cli", "mcp", "memory", "agent", "skill", "plugin": c.AddCommand(newListingInstallCmd(c.Name())) } } diff --git a/internal/cli/listing.go b/internal/cli/listing.go index 2979d6c..7245f91 100644 --- a/internal/cli/listing.go +++ b/internal/cli/listing.go @@ -33,23 +33,29 @@ type listingDetail struct { } // listingNounShorts gives each registry noun its own help line. +// Keys mirror api.ListingTypes (harness, cli, mcp, memory, agent, skill, plugin, loop). var listingNounShorts = map[string]string{ "harness": "Discover agent harnesses in the registry", "cli": "Discover official CLI tools in the registry", "mcp": "Discover MCP servers in the registry", + "memory": "Discover memory and context engines in the registry", "agent": "Discover agent SDKs and frameworks in the registry", "skill": "Discover agent skills in the registry", "plugin": "Discover agent plugins in the registry", "loop": "Discover reusable agent loops in the registry", } -// newListingNounCmd builds one registry noun (harness, cli, mcp, agent, -// skill, plugin, loop) with the shared search/list/show verbs, each scoped to -// its listing type via ?type= on the API. +// newListingNounCmd builds one registry noun from api.ListingTypes with the +// shared search/list/show verbs, each scoped to its listing type via ?type= +// on the API. func newListingNounCmd(listingType string) *cobra.Command { + short := listingNounShorts[listingType] + if short == "" { + short = fmt.Sprintf("Discover %s listings in the registry", listingType) + } cmd := &cobra.Command{ Use: listingType, - Short: listingNounShorts[listingType], + Short: short, } cmd.AddCommand( newListingSearchCmd(listingType), diff --git a/internal/cli/testdata/golden/agent-docs.json b/internal/cli/testdata/golden/agent-docs.json index 027dfff..0bc93a8 100644 --- a/internal/cli/testdata/golden/agent-docs.json +++ b/internal/cli/testdata/golden/agent-docs.json @@ -589,6 +589,81 @@ "description": "Show one mcp listing in full", "flags": [] }, + { + "path": "positronick memory", + "use": "positronick memory [flags]", + "description": "Discover memory and context engines in the registry", + "flags": [] + }, + { + "path": "positronick memory install", + "use": "positronick memory install \u003cslug\u003e [flags]", + "description": "Print (or run with --run) the official install command for one memory", + "flags": [ + { + "name": "run", + "shorthand": "", + "usage": "execute the install command via `sh -c` instead of printing it", + "default": "false" + } + ] + }, + { + "path": "positronick memory list", + "use": "positronick memory list [flags]", + "description": "List all memory listings", + "flags": [ + { + "name": "category", + "shorthand": "", + "usage": "only results in this category (case-insensitive)", + "default": "" + }, + { + "name": "limit", + "shorthand": "", + "usage": "maximum number of results", + "default": "20" + }, + { + "name": "sort", + "shorthand": "", + "usage": "sort order: relevance, name, downloads or newest", + "default": "name" + } + ] + }, + { + "path": "positronick memory search", + "use": "positronick memory search \u003cquery\u003e [flags]", + "description": "Search memory listings by fuzzy relevance", + "flags": [ + { + "name": "category", + "shorthand": "", + "usage": "only results in this category (case-insensitive)", + "default": "" + }, + { + "name": "limit", + "shorthand": "", + "usage": "maximum number of results", + "default": "20" + }, + { + "name": "sort", + "shorthand": "", + "usage": "sort order: relevance, name, downloads or newest", + "default": "relevance" + } + ] + }, + { + "path": "positronick memory show", + "use": "positronick memory show \u003cslug\u003e [flags]", + "description": "Show one memory listing in full", + "flags": [] + }, { "path": "positronick plugin", "use": "positronick plugin [flags]", diff --git a/internal/cli/testdata/golden/agent-docs.txt b/internal/cli/testdata/golden/agent-docs.txt index 5235a86..5adab68 100644 --- a/internal/cli/testdata/golden/agent-docs.txt +++ b/internal/cli/testdata/golden/agent-docs.txt @@ -1,6 +1,6 @@ # positronick — agent manual -`positronick` is the command-line client for positronick.com. It discovers agent capabilities: souls (installable SOUL.md personality files), a registry of official tooling (harnesses, CLIs, MCP servers, agents, skills, plugins, loops), and a `research` feed of what's new (articles, releases, links) so agents avoid stale knowledge. It is built to be driven by coding agents — pass `--json` to any command for stable machine-readable JSON on stdout, read progress and errors from stderr, and branch on the exit code. Read commands never prompt. Hidden admin commands (create/update for souls and listings, create/list for profiles, list/create/update/sync for blog feed sources, and create/update/list for blog posts) exist and appear in help and in these docs after logging in with an admin account. +`positronick` is the command-line client for positronick.com. It discovers agent capabilities: souls (installable SOUL.md personality files), a registry of official tooling (harnesses, CLIs, MCP servers, memory, agents, skills, plugins, loops), and a `research` feed of what's new (articles, releases, links) so agents avoid stale knowledge. It is built to be driven by coding agents — pass `--json` to any command for stable machine-readable JSON on stdout, read progress and errors from stderr, and branch on the exit code. Read commands never prompt. Hidden admin commands (create/update for souls and listings, create/list for profiles, list/create/update/sync for blog feed sources, and create/update/list for blog posts) exist and appear in help and in these docs after logging in with an admin account. Exit codes: @@ -404,6 +404,52 @@ Usage: `positronick mcp show [flags]` Show one mcp listing in full +## positronick memory + +Usage: `positronick memory [flags]` + +Discover memory and context engines in the registry + +## positronick memory install + +Usage: `positronick memory install [flags]` + +Print (or run with --run) the official install command for one memory + +Flags: + +- `--run` (default `false`): execute the install command via `sh -c` instead of printing it + +## positronick memory list + +Usage: `positronick memory list [flags]` + +List all memory listings + +Flags: + +- `--category` (default ``): only results in this category (case-insensitive) +- `--limit` (default `20`): maximum number of results +- `--sort` (default `name`): sort order: relevance, name, downloads or newest + +## positronick memory search + +Usage: `positronick memory search [flags]` + +Search memory listings by fuzzy relevance + +Flags: + +- `--category` (default ``): only results in this category (case-insensitive) +- `--limit` (default `20`): maximum number of results +- `--sort` (default `relevance`): sort order: relevance, name, downloads or newest + +## positronick memory show + +Usage: `positronick memory show [flags]` + +Show one memory listing in full + ## positronick plugin Usage: `positronick plugin [flags]` diff --git a/internal/cli/testdata/golden/mcp-tools-list.json b/internal/cli/testdata/golden/mcp-tools-list.json index 9b87f10..7a391aa 100644 --- a/internal/cli/testdata/golden/mcp-tools-list.json +++ b/internal/cli/testdata/golden/mcp-tools-list.json @@ -1,6 +1,6 @@ [ { - "description": "Search positronick.com's registry of verified agent tooling — harnesses, CLIs, MCP servers, agent SDKs, skills, plugins and loops — ranked by fuzzy relevance. Follow up with listing_show for the full record.", + "description": "Search positronick.com's registry of verified agent tooling — harnesses, CLIs, MCP servers, memory, agent SDKs, skills, plugins and loops — ranked by fuzzy relevance. Follow up with listing_show for the full record.", "inputSchema": { "additionalProperties": false, "properties": { @@ -24,6 +24,7 @@ "harness", "cli", "mcp", + "memory", "agent", "skill", "plugin", diff --git a/internal/mcpserver/instructions.go b/internal/mcpserver/instructions.go index 0e1d390..71c726b 100644 --- a/internal/mcpserver/instructions.go +++ b/internal/mcpserver/instructions.go @@ -10,7 +10,7 @@ const serverInstructions = `positronick is the registry of agent capabilities on Souls are installable SOUL.md personality files: soul_search → soul_show → soul_install. The wider registry of verified tooling (harnesses, CLIs, MCP -servers, agents, skills, plugins, loops): listing_search → listing_show. +servers, memory, agents, skills, plugins, loops): listing_search → listing_show. Search results are slim cards; always fetch the full record with the _show tool before acting on an entry. soul_install has side effects — it counts diff --git a/internal/mcpserver/listings.go b/internal/mcpserver/listings.go index d7a2fc8..2c10315 100644 --- a/internal/mcpserver/listings.go +++ b/internal/mcpserver/listings.go @@ -54,7 +54,7 @@ func addListingTools(srv *mcp.Server, opts Options) { mcp.AddTool(srv, &mcp.Tool{ Name: "listing_search", Description: "Search positronick.com's registry of verified agent tooling — harnesses, " + - "CLIs, MCP servers, agent SDKs, skills, plugins and loops — ranked by fuzzy " + + "CLIs, MCP servers, memory, agent SDKs, skills, plugins and loops — ranked by fuzzy " + "relevance. Follow up with listing_show for the full record.", InputSchema: inputSchema[listingSearchIn](func(s *jsonschema.Schema) { s.Properties["type"].Enum = enumOf(api.ListingTypes) diff --git a/internal/mockapi/admin.go b/internal/mockapi/admin.go index 7ffada7..a075edd 100644 --- a/internal/mockapi/admin.go +++ b/internal/mockapi/admin.go @@ -41,10 +41,12 @@ const ( ) // Enum fixtures mirroring src/lib/types.ts in the product repo. +// Listing categories come from api.ListingCategories so mockadmin validation +// stays in lockstep with the platform LISTING_CATEGORIES contract. var ( soulCategories = []string{"Technical", "Professional", "Creative", "Educational", "Wellness", "Research", "Experimental", "Playful"} soulFrameworks = []string{"hermes", "openclaw", "claude-code", "cursor"} - listingCategories = []string{"AI/ML", "DevOps", "Cloud", "Web", "Data", "Security", "Technical", "Productivity"} + listingCategories = api.ListingCategories statuses = []string{"draft", "pending", "published"} ) diff --git a/skills/positronick/SKILL.md b/skills/positronick/SKILL.md index 72960c5..156330b 100644 --- a/skills/positronick/SKILL.md +++ b/skills/positronick/SKILL.md @@ -2,8 +2,8 @@ name: positronick description: >- Discover and install agent capabilities from the positronick.com registry — - souls (SOUL.md personality files), harnesses, CLIs, MCP servers, agents, - skills, plugins, and loops. Use when asked to find or install a soul or + souls (SOUL.md personality files), harnesses, CLIs, MCP servers, memory, + agents, skills, plugins, and loops. Use when asked to find or install a soul or SOUL.md, browse positronick listings, set up a loop recipe, or whenever the positronick MCP tools or `positronick` CLI are available and the task involves agent tooling discovery. @@ -48,7 +48,7 @@ Souls are installable SOUL.md personality files for coding agents. ## Registry listings: search → show Everything else is a listing with one of these types: `harness`, `cli`, -`mcp`, `agent`, `skill`, `plugin`, `loop`. +`mcp`, `memory`, `agent`, `skill`, `plugin`, `loop`. 1. `listing_search` — scope with `type`, filter with `category`. Cards carry the official `installCmd` and verified `sourceUrl`.