@passioncode-ai/passioncode 0.1.10 → 0.1.12
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/CHANGELOG.md +34 -0
- package/CLA.md +28 -0
- package/COMMERCIAL-LICENSE.md +11 -0
- package/LICENSE +661 -155
- package/README.md +16 -13
- package/family.json +1 -1
- package/package.json +4 -2
- package/payload/.claude-plugin/marketplace.json +4 -4
- package/payload/manifest.json +10 -10
- package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +25 -4
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +24 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +24 -6
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +10 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +8 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +23 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +3 -0
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +23 -4
- package/payload/plugins/passioncode/.claude-plugin/plugin.json +2 -2
- package/payload/plugins/passioncode/hooks/repo-rules.js +11 -9
- package/payload/plugins/passioncode/skills/working-in-passioncode/SKILL.md +59 -31
- package/payload/plugins/passioncode/skills/working-in-passioncode/references/publishing.md +37 -27
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# PassionCode.ai launcher
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
The **PassionCode.ai launcher** (`@passioncode-ai/passioncode` on npm) installs and updates every
|
|
4
|
+
PassionCode.ai agent skill and plugin — the Fabric Agent Adapter, Observatory Log and the
|
|
5
|
+
organization's working rules — for Claude Code and every other agent on your machine, as one set.
|
|
6
|
+
It is how a teammate's agent gets the tools that build for Fabric, PassionCode.ai's CEO AI agent,
|
|
7
|
+
and it works on its own: no Fabric install, no account, no key.
|
|
7
8
|
|
|
8
9
|
```bash
|
|
9
10
|
npx @passioncode-ai/passioncode@latest update
|
|
@@ -27,8 +28,11 @@ out of it, even when they implement Fabric protocols.
|
|
|
27
28
|
no unscoped `passioncode` package on npm.
|
|
28
29
|
2. **Configure:** nothing to set, no account and no key. Background updates are on by default;
|
|
29
30
|
`npx @passioncode-ai/passioncode@latest config set update.auto off` turns them off.
|
|
30
|
-
3. **MCP:** none. The launcher installs skills and session hooks
|
|
31
|
-
|
|
31
|
+
3. **MCP:** none today, and not planned. The launcher installs skills and session hooks and is
|
|
32
|
+
driven by its CLI (`update`, `status`, `restore`, `uninstall`, `config`); it neither serves nor
|
|
33
|
+
calls an MCP server. The products it installs for, and the ones that serve MCP (Fabric
|
|
34
|
+
Switchboard, Fabric Inbox, Fabric Dashboards), document their own registration and proving
|
|
35
|
+
call; the Fabric Agent Adapter's quick start builds and calls one.
|
|
32
36
|
4. **Develop:** clone, then `npm test` — there are no dependencies and no lockfile, so there is
|
|
33
37
|
nothing to install (`npm ci` refuses to run here). `npm run vendor` builds `payload/` from the
|
|
34
38
|
members pinned in `family.json`; `claude plugin validate ./payload --strict` checks it. Start
|
|
@@ -93,9 +97,8 @@ reads a member from `<dir>/<repository name>`, and `PASSIONCODE_CHECKOUT_<MEMBER
|
|
|
93
97
|
|
|
94
98
|
## License
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
([CONTRIBUTING.md](CONTRIBUTING.md)).
|
|
100
|
+
Open source under the [GNU AGPL-3.0](LICENSE). A [commercial license](COMMERCIAL-LICENSE.md) is
|
|
101
|
+
available for use that does not meet the AGPL's terms — contact@passioncode.ai.
|
|
102
|
+
Versions before 0.1.12 were released under PolyForm Noncommercial or Internal Use (v0.1.5 to
|
|
103
|
+
v0.1.11) and MIT (v0.1.4 and earlier); each keeps the licence it was released under.
|
|
104
|
+
Contributions are accepted under [CLA.md](CLA.md) ([CONTRIBUTING.md](CONTRIBUTING.md)).
|
package/family.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/passioncode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Every PassionCode.ai agent skill — Fabric Agent Adapter, Observatory Log and the organisation's working rules — for Claude Code and every other agent on the machine, updated as one set.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"passioncode": "bin/passioncode.js"
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"family.json",
|
|
13
13
|
"README.md",
|
|
14
14
|
"LICENSE",
|
|
15
|
+
"COMMERCIAL-LICENSE.md",
|
|
16
|
+
"CLA.md",
|
|
15
17
|
"CHANGELOG.md",
|
|
16
18
|
"SECURITY.md"
|
|
17
19
|
],
|
|
@@ -32,7 +34,7 @@
|
|
|
32
34
|
"url": "git+https://github.com/passioncode-ai/passioncode.git"
|
|
33
35
|
},
|
|
34
36
|
"homepage": "https://github.com/passioncode-ai/passioncode",
|
|
35
|
-
"license": "
|
|
37
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial",
|
|
36
38
|
"author": {
|
|
37
39
|
"name": "PassionCode.ai",
|
|
38
40
|
"url": "https://passioncode.ai/"
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"name": "fabric-agent-adapter",
|
|
12
12
|
"displayName": "Fabric Agent Adapter",
|
|
13
13
|
"source": "./plugins/fabric-agent-adapter",
|
|
14
|
-
"version": "0.5.
|
|
14
|
+
"version": "0.5.3",
|
|
15
15
|
"description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles.",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "PassionCode.ai",
|
|
18
18
|
"url": "https://passioncode.ai/"
|
|
19
19
|
},
|
|
20
|
-
"license": "
|
|
20
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "observatory-log",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"name": "passioncode",
|
|
36
36
|
"displayName": "PassionCode.ai",
|
|
37
37
|
"source": "./plugins/passioncode",
|
|
38
|
-
"version": "0.1.
|
|
38
|
+
"version": "0.1.12",
|
|
39
39
|
"description": "The organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
|
|
40
40
|
"author": {
|
|
41
41
|
"name": "PassionCode.ai",
|
|
42
42
|
"url": "https://passioncode.ai/"
|
|
43
43
|
},
|
|
44
|
-
"license": "
|
|
44
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial"
|
|
45
45
|
}
|
|
46
46
|
]
|
|
47
47
|
}
|
package/payload/manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"family": "passioncode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"release": true,
|
|
5
5
|
"members": [
|
|
6
6
|
{
|
|
7
7
|
"name": "fabric-agent-adapter",
|
|
8
8
|
"displayName": "Fabric Agent Adapter",
|
|
9
9
|
"repo": "passioncode-ai/fabric-agent-adapter",
|
|
10
|
-
"ref": "v0.5.
|
|
11
|
-
"commit": "
|
|
12
|
-
"version": "0.5.
|
|
10
|
+
"ref": "v0.5.3",
|
|
11
|
+
"commit": "951b2910e450c491078486ddf022489d4742bbaf",
|
|
12
|
+
"version": "0.5.3",
|
|
13
13
|
"via": "clone of https://***@github.com/passioncode-ai/fabric-agent-adapter.git",
|
|
14
14
|
"skills": [
|
|
15
15
|
"adapting-projects-to-fabric",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"legacyMarketplaces": [
|
|
23
23
|
"fabric-agent-adapter"
|
|
24
24
|
],
|
|
25
|
-
"contentHash": "sha256:
|
|
25
|
+
"contentHash": "sha256:1b9d3028d72947aa3dc99f2cc979ffb9a6710a99ab0a89773f7de9ce673be210",
|
|
26
26
|
"description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles.",
|
|
27
27
|
"author": {
|
|
28
28
|
"name": "PassionCode.ai",
|
|
29
29
|
"url": "https://passioncode.ai/"
|
|
30
30
|
},
|
|
31
|
-
"license": "
|
|
31
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial"
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
"name": "observatory-log",
|
|
@@ -61,22 +61,22 @@
|
|
|
61
61
|
"name": "passioncode",
|
|
62
62
|
"displayName": "PassionCode.ai",
|
|
63
63
|
"repo": "passioncode-ai/passioncode",
|
|
64
|
-
"ref": "v0.1.
|
|
64
|
+
"ref": "v0.1.12",
|
|
65
65
|
"commit": null,
|
|
66
|
-
"version": "0.1.
|
|
66
|
+
"version": "0.1.12",
|
|
67
67
|
"via": "this repository",
|
|
68
68
|
"skills": [
|
|
69
69
|
"working-in-passioncode"
|
|
70
70
|
],
|
|
71
71
|
"legacyPluginIds": [],
|
|
72
72
|
"legacyMarketplaces": [],
|
|
73
|
-
"contentHash": "sha256:
|
|
73
|
+
"contentHash": "sha256:f0be91c0c114b6b5af7e9ae74459eb1c60a433ee0515b11468917f44fe4d6af0",
|
|
74
74
|
"description": "The organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
|
|
75
75
|
"author": {
|
|
76
76
|
"name": "PassionCode.ai",
|
|
77
77
|
"url": "https://passioncode.ai/"
|
|
78
78
|
},
|
|
79
|
-
"license": "
|
|
79
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial"
|
|
80
80
|
}
|
|
81
81
|
]
|
|
82
82
|
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"name": "fabric-agent-adapter",
|
|
4
4
|
"displayName": "Fabric Agent Adapter",
|
|
5
5
|
"description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles.",
|
|
6
|
-
"version": "0.5.
|
|
6
|
+
"version": "0.5.3",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|
|
9
9
|
"url": "https://passioncode.ai/"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/passioncode-ai/fabric-agent-adapter",
|
|
12
12
|
"repository": "https://github.com/passioncode-ai/fabric-agent-adapter",
|
|
13
|
-
"license": "
|
|
13
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial",
|
|
14
14
|
"keywords": [
|
|
15
15
|
"fabric",
|
|
16
16
|
"agent",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: adapting-projects-to-fabric
|
|
3
3
|
description: Use when adapting an existing agent, MCP server, A2A peer, HTTP service, or terminal CLI to the Fabric Agent Contract—choosing MCP/A2A/local-runner, scaffolding a provider bundle, or checking Fabric compatibility; also for «подключить проект к Fabric» or «сделать агента совместимым с Fabric». NOT for building the Fabric host/orchestrator, configuring an MCP client, or a brand-new agent — that is creating-fabric-agents.
|
|
4
|
-
license:
|
|
4
|
+
license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
5
5
|
compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned private fabric-agent-contract checkout. Works without those tools in an explicitly degraded structural-check mode.
|
|
6
6
|
metadata:
|
|
7
7
|
author: PassionCode.ai
|
|
8
|
-
version: "0.5.
|
|
8
|
+
version: "0.5.3"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
10
|
+
contract-commit: "2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Adapting projects to Fabric
|
|
@@ -70,7 +70,7 @@ Use exactly:
|
|
|
70
70
|
|
|
71
71
|
- contract version `0.1.0`;
|
|
72
72
|
- repository `https://github.com/passioncode-ai/fabric-agent-contract`;
|
|
73
|
-
- commit `
|
|
73
|
+
- commit `2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f`.
|
|
74
74
|
|
|
75
75
|
Read the pinned contract's guide
|
|
76
76
|
`docs/guides/connecting-compatible-agents.md`, the selected profile specification, and
|
|
@@ -179,6 +179,27 @@ unverified surfaces. If live Fabric admission/runtime does not yet exist, the la
|
|
|
179
179
|
remain `NOT VERIFIED`; the completed deliverable is an adaptation-ready provider bundle,
|
|
180
180
|
not a connected provider.
|
|
181
181
|
|
|
182
|
+
## The repository around the provider
|
|
183
|
+
|
|
184
|
+
**For a PassionCode.ai repository**, the adapted project's repository follows the organization's
|
|
185
|
+
[repository standard](https://github.com/passioncode-ai/fabric-workspace/blob/main/knowledge/repository-standard.md) (`knowledge/repository-standard.md`
|
|
186
|
+
in the organization's knowledge base; a clone has it at `fabric-workspace/knowledge/`) from its
|
|
187
|
+
first commit:
|
|
188
|
+
|
|
189
|
+
- `LICENSE`, `COMMERCIAL-LICENSE.md` and `CLA.md` copied byte for byte from the knowledge base's
|
|
190
|
+
`templates/`, and `AGPL-3.0-only OR LicenseRef-PassionCode-Commercial` in every manifest and
|
|
191
|
+
every SKILL.md `license:`; `SECURITY.md` when the repository is public;
|
|
192
|
+
- a README whose first heading is the full name, with `## Quick start for a new teammate`
|
|
193
|
+
(Install; Configure, key names only; MCP, the registration command and one proving tool call,
|
|
194
|
+
verified with a real client in a temporary config; Develop) and `## License`;
|
|
195
|
+
- an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
|
|
196
|
+
and a `CLAUDE.md` whose first line is `@AGENTS.md`.
|
|
197
|
+
|
|
198
|
+
org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
|
|
199
|
+
someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
|
|
200
|
+
choice, nothing about it is published or listed by the organization, and none of these files is
|
|
201
|
+
required of it — though the verified MCP quick start is still how anyone learns to drive it.
|
|
202
|
+
|
|
182
203
|
## Completion format
|
|
183
204
|
|
|
184
205
|
Report:
|
|
@@ -16,7 +16,7 @@ from urllib.parse import urlparse
|
|
|
16
16
|
|
|
17
17
|
CONTRACT_VERSION = "0.1.0"
|
|
18
18
|
CONTRACT_REPOSITORY = "https://github.com/passioncode-ai/fabric-agent-contract"
|
|
19
|
-
CONTRACT_COMMIT = "
|
|
19
|
+
CONTRACT_COMMIT = "2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f"
|
|
20
20
|
INTEROP_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
|
|
21
21
|
MCP_REVISION = "2026-07-28"
|
|
22
22
|
A2A_VERSION = "1.0"
|
|
@@ -11,14 +11,14 @@ description: >-
|
|
|
11
11
|
descriptor, the well-known document, the events feed and notifications; ships Python and Node
|
|
12
12
|
reference kits and a live conformance probe. NOT for a one-off script or cron job, a hosted
|
|
13
13
|
SaaS, the provider manifest itself (adapting-projects-to-fabric), or building Fabric Dashboards.
|
|
14
|
-
license:
|
|
14
|
+
license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
15
15
|
compatibility: Python 3.9+ or Node.js 20+ for the kits; the probe needs Python 3.9+. launchd steps are macOS-only (Linux services use lifecycle manager none until a systemd adapter exists). No network or package install; the contract checkout is optional.
|
|
16
16
|
metadata:
|
|
17
17
|
author: PassionCode.ai
|
|
18
|
-
version: "0.5.
|
|
18
|
+
version: "0.5.3"
|
|
19
19
|
contract-version: "0.1.0"
|
|
20
20
|
extension: "fabric-service/0.1"
|
|
21
|
-
extension-commit: "
|
|
21
|
+
extension-commit: "2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f"
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
# Building Fabric services
|
|
@@ -209,6 +209,27 @@ Then prove the lock: start a second copy by hand against the same data directory
|
|
|
209
209
|
must exit 75 while the first keeps serving. Prove recovery: `kill -9` the launchd pid;
|
|
210
210
|
the well-known document must answer again with a new pid within about 15 seconds.
|
|
211
211
|
|
|
212
|
+
## The repository around the service
|
|
213
|
+
|
|
214
|
+
**For a PassionCode.ai repository**, the service's repository follows the organization's
|
|
215
|
+
[repository standard](https://github.com/passioncode-ai/fabric-workspace/blob/main/knowledge/repository-standard.md) (`knowledge/repository-standard.md`
|
|
216
|
+
in the organization's knowledge base; a clone has it at `fabric-workspace/knowledge/`) from its
|
|
217
|
+
first commit:
|
|
218
|
+
|
|
219
|
+
- `LICENSE`, `COMMERCIAL-LICENSE.md` and `CLA.md` copied byte for byte from the knowledge base's
|
|
220
|
+
`templates/`, and `AGPL-3.0-only OR LicenseRef-PassionCode-Commercial` in every manifest and
|
|
221
|
+
every SKILL.md `license:`; `SECURITY.md` when the repository is public;
|
|
222
|
+
- a README whose first heading is the full name, with `## Quick start for a new teammate`
|
|
223
|
+
(Install; Configure, key names only; MCP, the registration command and one proving tool call,
|
|
224
|
+
verified with a real client in a temporary config; Develop) and `## License`;
|
|
225
|
+
- an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
|
|
226
|
+
and a `CLAUDE.md` whose first line is `@AGENTS.md`.
|
|
227
|
+
|
|
228
|
+
org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
|
|
229
|
+
someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
|
|
230
|
+
choice, nothing about it is published or listed by the organization, and none of these files is
|
|
231
|
+
required of it — though the verified MCP quick start is still how anyone learns to drive it.
|
|
232
|
+
|
|
212
233
|
## Migrating an existing service
|
|
213
234
|
|
|
214
235
|
Read [the migration reference](references/migrating-a-service.md) — the order of
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Being called: `fabric-interop/0.1`
|
|
2
2
|
|
|
3
|
-
Normative source: the Fabric Agent Contract's `docs/specification/interop.md` (DEC-0016, rulings DEC-0017)
|
|
3
|
+
Normative source: the Fabric Agent Contract's `docs/specification/interop.md` (DEC-0016, rulings DEC-0017 and DEC-0018)
|
|
4
4
|
at the commit this plugin pins. This page says how the kit implements it; where the two
|
|
5
5
|
differ, the contract is right.
|
|
6
6
|
|
|
@@ -50,15 +50,32 @@ response = server.handle(json_rpc_message) # POST /mcp body in, JSON re
|
|
|
50
50
|
- Log events about the work with `**fi.trace_ids(ctx.traceparent)` so the pair lands on
|
|
51
51
|
the event.
|
|
52
52
|
|
|
53
|
-
`McpToolServer` answers `
|
|
54
|
-
|
|
53
|
+
`McpToolServer` answers `initialize` and `ping` (clients such as Claude Code 2.1.285 still
|
|
54
|
+
open with a handshake, and a server that refuses it is marked failed), `server/discover`,
|
|
55
|
+
`tools/list` and `tools/call` with JSON responses. Every tool's `outputSchema` must be
|
|
56
|
+
rooted at `type: object` (DEC-0018): `add_tool` refuses any other root, because a client may
|
|
57
|
+
drop the whole tool list over one bad schema — an SDK-level test will not show it, so check
|
|
58
|
+
with the real client (below). A service already on an MCP SDK keeps
|
|
55
59
|
the SDK and uses only the helpers. The Node kit has the helpers and the same job file
|
|
56
60
|
format, without the dispatcher. `scripts/sample_service.py` is the worked example:
|
|
57
61
|
`sample.echo` answers at once, `sample.draft` is a job that stops for a title choice.
|
|
58
62
|
|
|
59
|
-
The contract owner's rulings
|
|
60
|
-
self-contained
|
|
61
|
-
its `structuredContent` always conforms; an event about
|
|
63
|
+
The contract owner's rulings the kit follows: a job tool's `outputSchema` is the
|
|
64
|
+
self-contained `{type: object, oneOf: [result envelope, job handle]}` (`job_tool_output_schema`,
|
|
65
|
+
DEC-0017 amended by DEC-0018), so its `structuredContent` always conforms; an event about
|
|
66
|
+
untraced work carries no trace pair.
|
|
67
|
+
|
|
68
|
+
**Check with a real client**, never only an SDK: `FABRIC_REAL_CLIENT=1 python3 -m unittest
|
|
69
|
+
test.test_real_client` in the adapter repository, or by hand — a throwaway directory, a
|
|
70
|
+
temporary `--mcp-config` (mode 600, the token in a header) and `--strict-mcp-config`, so
|
|
71
|
+
the operator's own configuration is not touched:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
claude -p "Reply with the single word OK." --strict-mcp-config --mcp-config "$TMP/mcp.json" \
|
|
75
|
+
--output-format stream-json --verbose --max-turns 1
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The first `system/init` event lists `mcp_servers` (`connected`) and the `mcp__<server>__*` tools.
|
|
62
79
|
|
|
63
80
|
## What the probe checks
|
|
64
81
|
|
|
@@ -66,6 +83,7 @@ its `structuredContent` always conforms; an event about untraced work carries no
|
|
|
66
83
|
|
|
67
84
|
| Rule | PASS when | NOT_RUN when |
|
|
68
85
|
|---|---|---|
|
|
86
|
+
| `interop.output-schema-object` | every listed tool's `outputSchema`, when present, has root `type: object` (FAC-SEM-023) | no MCP surface |
|
|
69
87
|
| `interop.manifest-link` | the manifest `fabricManifest` names carries the service key with this `<id>.<instance>` (G-07) | the descriptor names no manifest |
|
|
70
88
|
| `interop.well-known-capabilities` | every name in `surfaces.mcp.capabilities` is a manifest capability | the surface lists none |
|
|
71
89
|
| `interop.tools-match` | every `mcp` capability is served as its tool, schemas equal to the files beside the manifest (matched by `$id`; a job's output wrapped in the DEC-0017 union), annotations derived | no manifest, no MCP surface, or a schema is not beside the manifest |
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# fabric-service/0.1 — wire reference
|
|
2
2
|
|
|
3
|
-
Pinned to `fabric-agent-contract` commit `
|
|
3
|
+
Pinned to `fabric-agent-contract` commit `2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f`
|
|
4
4
|
(`docs/specification/service.md`, DEC-0015). The contract's schemas are normative;
|
|
5
5
|
this page is the working summary. Extension key:
|
|
6
6
|
`https://fabric.passioncode.ai/agent-contract/extensions/service/0.1`.
|
|
@@ -287,7 +287,7 @@ class Probe:
|
|
|
287
287
|
self.add("interop.well-known-capabilities", "FAIL" if extra else "PASS",
|
|
288
288
|
("listed but not in the manifest: " + ", ".join(extra)) if extra else "%d listed, all in the manifest" % len(listed))
|
|
289
289
|
if not mcp_surface or not self.token:
|
|
290
|
-
for rule in ("interop.tools-match", "interop.job-tools", "interop.unknown-job", "interop.trace-propagation"):
|
|
290
|
+
for rule in ("interop.output-schema-object", "interop.tools-match", "interop.job-tools", "interop.unknown-job", "interop.trace-propagation"):
|
|
291
291
|
self.add(rule, "NOT_RUN", "no MCP surface" if not mcp_surface else "no readable token")
|
|
292
292
|
else:
|
|
293
293
|
try:
|
|
@@ -298,12 +298,21 @@ class Probe:
|
|
|
298
298
|
if listing is not None:
|
|
299
299
|
sent = self.sent_traceparent
|
|
300
300
|
tools = {t.get("name"): t for t in (listing.get("result") or {}).get("tools", []) if isinstance(t, dict)}
|
|
301
|
+
self.object_root_rule(tools)
|
|
301
302
|
self.tools_match_rule(capabilities, manifest_path, tools)
|
|
302
303
|
self.job_tools_rule(capabilities, tools)
|
|
303
304
|
self.unknown_job_rule(list(tools))
|
|
304
305
|
self.trace_rule(listing, sent)
|
|
305
306
|
self.events_trace_rule(self.events)
|
|
306
307
|
|
|
308
|
+
def object_root_rule(self, tools: Dict[str, Any]) -> None:
|
|
309
|
+
"""FAC-SEM-023 (DEC-0018): every listed tool's outputSchema, when present, has root type object —
|
|
310
|
+
a client may refuse the whole tools/list otherwise, whatever the SDK in the tests accepted."""
|
|
311
|
+
bad = sorted(n for n, t in tools.items() if "outputSchema" in t and (t.get("outputSchema") or {}).get("type") != "object")
|
|
312
|
+
with_schema = sum(1 for t in tools.values() if "outputSchema" in t)
|
|
313
|
+
self.add("interop.output-schema-object", "FAIL" if bad else "PASS",
|
|
314
|
+
("outputSchema root is not type object: " + ", ".join(bad)) if bad else "%d outputSchemas, every root type object" % with_schema)
|
|
315
|
+
|
|
307
316
|
def tools_match_rule(self, capabilities: List[Dict[str, Any]], manifest_path: Optional[Path], tools: Dict[str, Any]) -> None:
|
|
308
317
|
"""FAC-SEM-017: each mcp capability is served as the tool of its name, with its schemas and derived annotations."""
|
|
309
318
|
served = [c for c in capabilities if (c.get("profile") or {}).get("kind") == "mcp"]
|
|
@@ -66,10 +66,17 @@ export function expectedAnnotations(effect, idempotency) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// DEC-0017: a job-backed tool serves oneOf[result envelope, job handle]; the manifest keeps the pure output schema.
|
|
69
|
-
export const jobToolOutputSchema = (outputSchema) => ({ oneOf: [{ type: 'object', required: [...ENVELOPE_REQUIRED], properties: { output: outputSchema } }, JOB_HANDLE_SCHEMA] });
|
|
69
|
+
export const jobToolOutputSchema = (outputSchema) => ({ type: 'object', oneOf: [{ type: 'object', required: [...ENVELOPE_REQUIRED], properties: { output: outputSchema } }, JOB_HANDLE_SCHEMA] });
|
|
70
70
|
export const isJobCapability = (capability) => capability.job === true || capability.extensions?.[EXTENSION_KEY]?.job === true;
|
|
71
71
|
|
|
72
|
+
// DEC-0018 (FAC-SEM-023): an outputSchema is rooted at type object; a client may reject the whole
|
|
73
|
+
// tools/list when one tool's outputSchema is, say, a bare oneOf.
|
|
74
|
+
export function requireObjectRoot(name, outputSchema) {
|
|
75
|
+
if (!outputSchema || typeof outputSchema !== 'object' || outputSchema.type !== 'object') throw new InteropError(`Tool ${name}: an outputSchema must have root type "object".`);
|
|
76
|
+
}
|
|
77
|
+
|
|
72
78
|
export function toolForCapability(capability, inputSchema, outputSchema, title) {
|
|
79
|
+
requireObjectRoot(capability.name, outputSchema);
|
|
73
80
|
const served = isJobCapability(capability) ? jobToolOutputSchema(outputSchema) : outputSchema;
|
|
74
81
|
const tool = { name: capability.name, inputSchema, outputSchema: served, annotations: expectedAnnotations(capability.effect, capability.idempotency) };
|
|
75
82
|
if (capability.description) tool.description = capability.description;
|
|
@@ -30,6 +30,10 @@ import fabric_service as _fs # noqa: E402
|
|
|
30
30
|
PROTOCOL = "fabric-interop/0.1"
|
|
31
31
|
EXTENSION_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
|
|
32
32
|
MCP_REVISION = "2026-07-28"
|
|
33
|
+
# Revisions a client may name in `initialize`. MCP 2026-07-28 needs no handshake, but clients
|
|
34
|
+
# built on earlier revisions (Claude Code 2.1.285 among them) still open with one, and a server
|
|
35
|
+
# that refuses it is marked failed before tools/list is ever called.
|
|
36
|
+
HANDSHAKE_REVISIONS = ("2025-03-26", "2025-06-18", "2025-11-25", "2026-07-28")
|
|
33
37
|
CONTRACT_VERSION = "0.1.0"
|
|
34
38
|
JOB_STATES = ("working", "input_required", "completed", "failed", "cancelled")
|
|
35
39
|
OUTCOMES = ("succeeded", "partial", "failed", "cancelled", "blocked")
|
|
@@ -112,10 +116,18 @@ def expected_annotations(effect: str, idempotency: str) -> Dict[str, bool]:
|
|
|
112
116
|
def job_tool_output_schema(output_schema: Dict[str, Any]) -> Dict[str, Any]:
|
|
113
117
|
"""DEC-0017: a job-backed tool's outputSchema is oneOf[result envelope, job handle], self-contained,
|
|
114
118
|
so structuredContent always conforms; the manifest's capability keeps the pure output schema."""
|
|
115
|
-
return {"
|
|
119
|
+
return {"type": "object",
|
|
120
|
+
"oneOf": [{"type": "object", "required": list(ENVELOPE_REQUIRED), "properties": {"output": output_schema}},
|
|
116
121
|
JOB_HANDLE_SCHEMA]}
|
|
117
122
|
|
|
118
123
|
|
|
124
|
+
def require_object_root(name: str, output_schema: Any) -> None:
|
|
125
|
+
"""DEC-0018 (FAC-SEM-023): an outputSchema is rooted at type object. MCP requires it, and a client
|
|
126
|
+
may reject the WHOLE tools/list when one tool's outputSchema is, say, a bare oneOf."""
|
|
127
|
+
if not isinstance(output_schema, dict) or output_schema.get("type") != "object":
|
|
128
|
+
raise InteropError("Tool %s: an outputSchema must have root type \"object\"." % name)
|
|
129
|
+
|
|
130
|
+
|
|
119
131
|
def is_job_capability(capability: Dict[str, Any]) -> bool:
|
|
120
132
|
block = (capability.get("extensions") or {}).get(EXTENSION_KEY) or {}
|
|
121
133
|
return capability.get("job") is True or block.get("job") is True
|
|
@@ -125,6 +137,7 @@ def tool_for_capability(capability: Dict[str, Any], input_schema: Dict[str, Any]
|
|
|
125
137
|
title: Optional[str] = None) -> Dict[str, Any]:
|
|
126
138
|
"""The MCP tool for one manifest capability: its name, its input schema as it is, derived annotations,
|
|
127
139
|
and its output schema — wrapped in the job union when the capability is a job (DEC-0017)."""
|
|
140
|
+
require_object_root(capability["name"], output_schema)
|
|
128
141
|
tool: Dict[str, Any] = {
|
|
129
142
|
"name": capability["name"],
|
|
130
143
|
"inputSchema": input_schema,
|
|
@@ -391,6 +404,8 @@ class McpToolServer:
|
|
|
391
404
|
def add_tool(self, tool: Dict[str, Any], handler: Handler) -> None:
|
|
392
405
|
if tool["name"] in self.handlers:
|
|
393
406
|
raise InteropError("Tool %s is served twice." % tool["name"])
|
|
407
|
+
if "outputSchema" in tool:
|
|
408
|
+
require_object_root(tool["name"], tool["outputSchema"])
|
|
394
409
|
self.tools.append(tool)
|
|
395
410
|
self.handlers[tool["name"]] = handler
|
|
396
411
|
|
|
@@ -423,6 +438,13 @@ class McpToolServer:
|
|
|
423
438
|
meta = params.get("_meta") or {}
|
|
424
439
|
span = child_traceparent(meta.get("traceparent"))
|
|
425
440
|
method = message["method"]
|
|
441
|
+
if method == "initialize":
|
|
442
|
+
asked = params.get("protocolVersion")
|
|
443
|
+
return {"jsonrpc": "2.0", "id": rid, "result": {
|
|
444
|
+
"protocolVersion": asked if asked in HANDSHAKE_REVISIONS else MCP_REVISION,
|
|
445
|
+
"capabilities": {"tools": {"listChanged": False}}, "serverInfo": self.info}}
|
|
446
|
+
if method == "ping":
|
|
447
|
+
return {"jsonrpc": "2.0", "id": rid, "result": {}}
|
|
426
448
|
if method == "server/discover":
|
|
427
449
|
return {"jsonrpc": "2.0", "id": rid, "result": {"resultType": "complete", "capabilities": {"tools": {}},
|
|
428
450
|
"serverInfo": self.info, "_meta": {"traceparent": span}}}
|
|
@@ -187,6 +187,9 @@ def make_handler(svc: Service):
|
|
|
187
187
|
except fs.ServiceError as exc:
|
|
188
188
|
return self._send(400, {"error": str(exc)})
|
|
189
189
|
return self._send(200, page)
|
|
190
|
+
if url.path == "/mcp":
|
|
191
|
+
# No server-to-client stream here: Streamable HTTP says 405 for a GET.
|
|
192
|
+
return self._send(405, {"error": "POST JSON-RPC to /mcp."}, {"Allow": "POST"})
|
|
190
193
|
if url.path == "/fabric/v1/login":
|
|
191
194
|
cookie = svc.codes.redeem((query.get("code") or [None])[0])
|
|
192
195
|
if not cookie:
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: creating-fabric-agents
|
|
3
3
|
description: Use when designing and building a NEW agent or provider that must be Fabric-compatible from its first commit — «создай агента, совместимого с фабрикой Passion Code», «новый агент под Fabric», "create a fabric-compatible agent", "build a new Fabric provider", "fabric-ready agent from scratch". Runs the intake grill (capability, named consumer, workflow-or-agent, MCP/A2A/local-runner profile, effect declarations), distils source projects into a knowledge pack whose recorded failures become planted eval fixtures, scaffolds the pinned contract bundle, and sets the two-clock eval expectation with the conformance report. NOT for adapting an existing project (use adapting-projects-to-fabric), building the Fabric host or orchestrator, or generic agent design where Fabric compatibility is not part of the request.
|
|
4
|
-
license:
|
|
4
|
+
license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
5
5
|
compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned private fabric-agent-contract checkout. Works without those tools in an explicitly degraded structural-check mode. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
|
|
6
6
|
metadata:
|
|
7
7
|
author: PassionCode.ai
|
|
8
|
-
version: "0.5.
|
|
8
|
+
version: "0.5.3"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
10
|
+
contract-commit: "2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Creating Fabric-compatible agents
|
|
@@ -80,6 +80,25 @@ that would show it met, written now, before the implementation. The input corpus
|
|
|
80
80
|
authored now: it grows from real traces once the provider runs, and the step-1 fixtures
|
|
81
81
|
are its seed because a source project's recorded failures count as production.
|
|
82
82
|
|
|
83
|
+
**For a PassionCode.ai repository**, the new agent's repository follows the organization's
|
|
84
|
+
[repository standard](https://github.com/passioncode-ai/fabric-workspace/blob/main/knowledge/repository-standard.md) (`knowledge/repository-standard.md`
|
|
85
|
+
in the organization's knowledge base; a clone has it at `fabric-workspace/knowledge/`) from its
|
|
86
|
+
first commit:
|
|
87
|
+
|
|
88
|
+
- `LICENSE`, `COMMERCIAL-LICENSE.md` and `CLA.md` copied byte for byte from the knowledge base's
|
|
89
|
+
`templates/`, and `AGPL-3.0-only OR LicenseRef-PassionCode-Commercial` in every manifest and
|
|
90
|
+
every SKILL.md `license:`; `SECURITY.md` when the repository is public;
|
|
91
|
+
- a README whose first heading is the full name, with `## Quick start for a new teammate`
|
|
92
|
+
(Install; Configure, key names only; MCP, the registration command and one proving tool call,
|
|
93
|
+
verified with a real client in a temporary config; Develop) and `## License`;
|
|
94
|
+
- an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
|
|
95
|
+
and a `CLAUDE.md` whose first line is `@AGENTS.md`.
|
|
96
|
+
|
|
97
|
+
org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
|
|
98
|
+
someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
|
|
99
|
+
choice, nothing about it is published or listed by the organization, and none of these files is
|
|
100
|
+
required of it — though the verified MCP quick start is still how anyone learns to drive it.
|
|
101
|
+
|
|
83
102
|
## Step 3 — scaffold the provider bundle
|
|
84
103
|
|
|
85
104
|
Reuse the sibling skill's scaffolder against the new skeleton with the profile chosen at
|
|
@@ -93,7 +112,7 @@ python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/adapt_project.py
|
|
|
93
112
|
--schema-base <immutable-base-uri>
|
|
94
113
|
```
|
|
95
114
|
|
|
96
|
-
Pin exactly contract `0.1.0` at commit `
|
|
115
|
+
Pin exactly contract `0.1.0` at commit `2ea54f70c161c8b3bf7ec3c567735c7a2d4ce12f` and
|
|
97
116
|
read the pinned guide before implementing protocol details. If this skill is installed
|
|
98
117
|
without its sibling, the scaffolder is absent: create the bundle by hand from the pinned
|
|
99
118
|
contract's `docs/guides/connecting-compatible-agents.md` and mark the structural check
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "passioncode",
|
|
4
4
|
"displayName": "PassionCode.ai",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.12",
|
|
6
6
|
"description": "The organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check at session start that keeps the PassionCode.ai skill set current.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|
|
9
9
|
"url": "https://passioncode.ai/"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/passioncode-ai/passioncode",
|
|
12
|
-
"license": "
|
|
12
|
+
"license": "AGPL-3.0-only OR LicenseRef-PassionCode-Commercial"
|
|
13
13
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* The read-first reminder. In a passioncode-ai repository an
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
* The read-first reminder: the protocol of Fabric ADR-0093. In a passioncode-ai repository an
|
|
4
|
+
* agent reads the organization's knowledge base (fabric-workspace/knowledge/, published at
|
|
5
|
+
* wiki.passioncode.ai/knowledge) and the repository's AGENTS.md before its first edit, and after
|
|
6
|
+
* the work updates the knowledge base page that owns any cross-repository fact it changed. A rule
|
|
7
|
+
* an agent has to remember is a rule it forgets in the one repository where it mattered, so the
|
|
8
|
+
* session start says it, in one line, and only where it applies. Anything unexpected (no git, no
|
|
9
|
+
* origin, a slow git) prints nothing: a reminder must never cost a session.
|
|
9
10
|
*/
|
|
10
11
|
const { execFileSync } = require('child_process');
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
+
const KNOWLEDGE = 'fabric-workspace/knowledge/';
|
|
14
|
+
const KNOWLEDGE_WEB = 'https://wiki.passioncode.ai/knowledge';
|
|
13
15
|
|
|
14
16
|
/** The GitHub organization an origin URL belongs to, when it is passioncode-ai; else null. */
|
|
15
17
|
function orgOf(url) {
|
|
@@ -30,7 +32,7 @@ function originOf(dir) {
|
|
|
30
32
|
/** The one line to print at session start in `dir`, or null. */
|
|
31
33
|
function rulesLine(dir) {
|
|
32
34
|
if (!orgOf(originOf(dir))) return null;
|
|
33
|
-
return `[passioncode] passioncode-ai repository: read
|
|
35
|
+
return `[passioncode] passioncode-ai repository: read the knowledge base (${KNOWLEDGE} or ${KNOWLEDGE_WEB}), then this repository's AGENTS.md, before the first edit; after the work, update the knowledge base page that owns any cross-repository fact you changed.`;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
module.exports = {
|
|
38
|
+
module.exports = { KNOWLEDGE, KNOWLEDGE_WEB, orgOf, rulesLine };
|