@passioncode-ai/fabric-agent-adapter 0.69.1 → 0.70.0
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 +22 -0
- package/package.json +1 -1
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +91 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +5 -5
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +5 -3
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +3 -2
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +3 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +26 -5
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +44 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.70.0 (2026-11-06)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- working-with-fabric: on macOS, with `PASSIONCODE_APPLICATIONS` set to a folder starting with `/`, `status` looks for
|
|
8
|
+
`Fabric.app` only there, as the launcher installs every app in that one folder (its `lib/catalog.js` `appsDir`); an
|
|
9
|
+
empty or relative value keeps the default order (FAA-237, the launcher's reading of its PR #277, board thread
|
|
10
|
+
`cb1df3d5`). `status` names root's copy outside the home "the administrator's copy, which the launcher does not
|
|
11
|
+
update" (`administratorsCopy` and `launcherFolder` in `--json`); a launcher's folder that already holds another
|
|
12
|
+
user's `Fabric.app` is not proposed: `status` says to unset the variable and use `~/Applications`, as the launcher's
|
|
13
|
+
PR #277 advises for a bundle the user cannot write (`bd22566`, open: launcher 0.2.75 with the variable unset
|
|
14
|
+
installs into `/Applications`, so the person puts the copy in `~/Applications`); the label is about the copy
|
|
15
|
+
`status` found, not the registered command, and running it stays allowed. A folder this user cannot enter reads as
|
|
16
|
+
holding no copy: `status` names it in its install advice instead of stopping on Python's `PermissionError` (before
|
|
17
|
+
Python 3.14). With the folder set, `status` names it in its install advice and its `fabric-mcp:` line, never
|
|
18
|
+
`~/Applications` unless that folder holds another user's copy (above). The SKILL.md Step 0 row and the
|
|
19
|
+
`a-second-user-of-a-mac-registers-fabric` eval name the folder.
|
|
20
|
+
- `references/project-board.md` and `references/on-demand-session.md` name Fabric's `board.read-at-start` rule for the
|
|
21
|
+
slot condition of a gate decline: Fabric took it from the adapter into its board rule (main `dbfbfce4`) and into
|
|
22
|
+
`com.enroll`'s own tool description (main `12d57bdd`), from its next release, so a live session gets one instruction
|
|
23
|
+
(FAA-231).
|
|
24
|
+
|
|
3
25
|
## 0.69.1 (2026-11-06)
|
|
4
26
|
|
|
5
27
|
The tag `v0.69.0` was never published: its release run
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/fabric-agent-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.70.0",
|
|
4
4
|
"description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
|
|
@@ -3,7 +3,7 @@
|
|
|
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; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.70.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|
|
9
9
|
"url": "https://passioncode.ai/"
|
|
@@ -5,7 +5,7 @@ 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 fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode.
|
|
6
6
|
metadata:
|
|
7
7
|
author: PassionCode.ai
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.70.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
10
|
contract-commit: "3830a3353923e20f826d466610e1c479f120bec0"
|
|
11
11
|
---
|
|
@@ -14,7 +14,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
14
14
|
compatibility: Python 3.9+ or Node.js 20+ for the kits (Node.js 22+ for a Linux service's instance lock); the probe needs Python 3.9+. launchd steps are macOS-only; Linux services install a systemd user unit and Windows ones a per-user Scheduled Task with the kits' writers. Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
|
|
15
15
|
metadata:
|
|
16
16
|
author: PassionCode.ai
|
|
17
|
-
version: "0.
|
|
17
|
+
version: "0.70.0"
|
|
18
18
|
contract-version: "0.1.0"
|
|
19
19
|
extension: "fabric-service/0.1"
|
|
20
20
|
extension-commit: "3830a3353923e20f826d466610e1c479f120bec0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fabric-service-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.70.0",
|
|
4
4
|
"about": "The service kit's modules in dependency order: a module imports only modules listed before it. Written by fabric_kit.py build; edit the order and layers by hand, never the digests.",
|
|
5
5
|
"python": [
|
|
6
6
|
{
|
|
@@ -5496,6 +5496,96 @@
|
|
|
5496
5496
|
"fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
|
|
5497
5497
|
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
5498
5498
|
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
5499
|
+
},
|
|
5500
|
+
"0.70.0": {
|
|
5501
|
+
"fabric_jsvalue.py": "052f1b40bab18600b3731dbdca27cbff17588c21d9458fa6241efd1340d0e09a",
|
|
5502
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
5503
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
5504
|
+
"fabric_secrets.py": "53c5df8d6f2c87433c288853bfaad9a43f544789bdf1e39bda8d3ff14485c89d",
|
|
5505
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
5506
|
+
"fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
|
|
5507
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
5508
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
5509
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
5510
|
+
"fabric_estate.py": "0737ae1d1bb12586bae49de1f15ef1348bc207b5007cced9dad4c55bb5870874",
|
|
5511
|
+
"fabric_mandate.py": "fb19ce5359606bd6e3352ab02b2a12f49605ec6e5c49b55cce1f79c831325044",
|
|
5512
|
+
"fabric_delegation.py": "716f0ff7ac01095f54b24970beac1962128e8ff33026b243fc0467876142ab03",
|
|
5513
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
5514
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
5515
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
5516
|
+
"fabric_package.py": "1f2b9e73b2b32a7e1e887d4b69f87c6e21b4d1227ad924a235334223e6dd51ca",
|
|
5517
|
+
"fabric_protocols.py": "42529a7df4d753544d2e37a18ec81266d6e1e80477cb8558a3ab521416d45791",
|
|
5518
|
+
"fabric_actions.py": "ae21d1199614c4f74d1dcd1e0a87107a9d52434cd9e2d64aa24a7b1e196fb723",
|
|
5519
|
+
"fabric_connections.py": "7de55cb7d808f756d650effabfaa12027d40a5bd5d2f6d448c32969d47529ecb",
|
|
5520
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
5521
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
5522
|
+
"fabric_lock.py": "dfaf92a7086cbb097d832a305869b7964e2d2a9da0e3365c8ec1685a7bb4fef9",
|
|
5523
|
+
"fabric_usage.py": "5cc2e03ccc5b06e81987ed7bc3eead7a7e6cbf1a7cb8e05c6c64315eed9d91b6",
|
|
5524
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
5525
|
+
"fabric_descriptor.py": "c08c91ca0960c694ed7677e7e9247d6307c7b63f37a7e614db022c080cd92ccc",
|
|
5526
|
+
"fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
|
|
5527
|
+
"fabric_auth.py": "74ccc10239b65a3567cec5213a7b344cbd88d6eee1fd1c5f7be1d6daf985993c",
|
|
5528
|
+
"fabric_events.py": "cd94b186e2a8f8cb4ad2b6bd44d83ec5021933075e28c5d655f387f16dff42b8",
|
|
5529
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
5530
|
+
"fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
|
|
5531
|
+
"fabric_service.py": "4b13f90aa26881fbfe0d9f3077fbb3552f3ce0a3c59279d6baa92df872867d8d",
|
|
5532
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
5533
|
+
"fabric_interop_rules.py": "9bdb7989e57b9d0b0d15eb9982d396499823a5e2c528230b71c67866c736e0f5",
|
|
5534
|
+
"fabric_interop.py": "d27e31834611a37cf2a117746f2f60fc187ccc48718eb546ff923c5d0fce7dea",
|
|
5535
|
+
"fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
|
|
5536
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
5537
|
+
"fabric_board_link.py": "052bc3c474ece835bb428c37ca864810d04e394d991ddb9c93989a7a825073a5",
|
|
5538
|
+
"fabric_board_slots.py": "92b065e3df935547cf46653245f1129e4a1f8b59b0a41e8fa9ffdc41596ee592",
|
|
5539
|
+
"fabric_board_session.py": "1fd3c3884f3044b56a40f78b33ff88dcac9272284f6681ea359c662879826f7e",
|
|
5540
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
5541
|
+
"fabric_board.py": "f86c40b8291f8c818f6924910f4022b013d5fd107abaf8825dd0aa6c5e418638",
|
|
5542
|
+
"fabric_http.py": "8b443477dc29d5a1fe908b9dccaf68f05fab837eae0670074051a1dd2dfb71ad",
|
|
5543
|
+
"fabric_openrouter.py": "d06a40f83f2fb8bd441ab3485ae882f1fbdd9cd7a9c1ec175eb47cb8ae2cce81",
|
|
5544
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
5545
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
5546
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
5547
|
+
"fabric-secrets.mjs": "5822a7e25ecf2e87b5bab4f99c1074af7379d8b49e2784283b5d7d8c25022cde",
|
|
5548
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
5549
|
+
"fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
|
|
5550
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
5551
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
5552
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
5553
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
5554
|
+
"fabric-mandate.mjs": "b70f3de03a659e60bba7b5a0580b3582825cd7b8a8e30a28c79621919ec5b7bb",
|
|
5555
|
+
"fabric-delegation.mjs": "8f63a63aab9a8746b7f664720b87e9398137d01b8d50880974d5070038e414a0",
|
|
5556
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
5557
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
5558
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
5559
|
+
"fabric-package.mjs": "8cfb32eebbd4e1bcf28a6e4437344ae2dd54479b0e7c820f002e9f94ab9ecaad",
|
|
5560
|
+
"fabric-protocols.mjs": "5fd8765fdd3e507cc85c108492682bf5cbb4ae08873245c50374fb01d33c75e4",
|
|
5561
|
+
"fabric-actions.mjs": "06e42f5e24c96311d5417d0d1effa93479ef7128a7ab139903b2bcf59253f6be",
|
|
5562
|
+
"fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
|
|
5563
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
5564
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
5565
|
+
"fabric-lock.mjs": "02cf833c97a07a8b3c788f9c93f70c4b9c9066efa25318dccf0038e0fc79717f",
|
|
5566
|
+
"fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
|
|
5567
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
5568
|
+
"fabric-descriptor.mjs": "7c0ef7d245c037eb9e859ab481f6e66fb3e5ef2cf6d1e0a8d32b6d303097ca43",
|
|
5569
|
+
"fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
|
|
5570
|
+
"fabric-auth.mjs": "10ff37a39f9e3adb4d0f05db66c45f49b10eef582e9220c213907964109d7e52",
|
|
5571
|
+
"fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
|
|
5572
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
5573
|
+
"fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
|
|
5574
|
+
"fabric-service.mjs": "297fef11ac76dc831d12d24c5581b7a93587c300c20664db703b3f924c0f9f1a",
|
|
5575
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
5576
|
+
"fabric-interop-rules.mjs": "b4e386c359a48e49ffff8a842215760c18a7c98a0af4754a808a35fb2820d458",
|
|
5577
|
+
"fabric-interop.mjs": "989040aed33b2712a258244192bdf70e811a51076aa1b62f2adc2a6db051dbed",
|
|
5578
|
+
"fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
|
|
5579
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
5580
|
+
"fabric-board-link.mjs": "6e7d51f4fd9730d93455f50dd30e97907b8b9e1ca65a1d69f036f2e8ec05cc0d",
|
|
5581
|
+
"fabric-board-slots.mjs": "fc77a3c6e62aa18575568bef4f703cce2edaba8cef648813ebcdbb21bdc27a11",
|
|
5582
|
+
"fabric-board-session.mjs": "18555962e5f6d18080a3e7971c4c6640452542f6042d07917e7ebc073721c415",
|
|
5583
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
5584
|
+
"fabric-board.mjs": "15fbb4441fcf7d2f285375b75de64ad6cffea129c515084c110245376b07775f",
|
|
5585
|
+
"fabric-http.mjs": "317d7668c291570d4d93bc73295312f2853eb93940f4cf754a4a4619cb87ace9",
|
|
5586
|
+
"fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
|
|
5587
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
5588
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
5499
5589
|
}
|
|
5500
5590
|
},
|
|
5501
5591
|
"data": {
|
|
@@ -5,7 +5,7 @@ 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 fabric-agent-contract checkout (a public repository). 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.
|
|
8
|
+
version: "0.70.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
10
|
contract-commit: "3830a3353923e20f826d466610e1c479f120bec0"
|
|
11
11
|
---
|
|
@@ -18,7 +18,7 @@ compatibility: >-
|
|
|
18
18
|
this plugin; Codex/Kimi use command hooks; Hermes/OpenCode/OpenClaw/Pi use native context plugins after opt-in.
|
|
19
19
|
metadata:
|
|
20
20
|
author: PassionCode.ai
|
|
21
|
-
version: "0.
|
|
21
|
+
version: "0.70.0"
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
# Working with Fabric
|
|
@@ -50,7 +50,7 @@ By hand, when Python is absent: Fabric publishes `hub.json` in its folder while
|
|
|
50
50
|
|
|
51
51
|
| OS | Fabric's folder (`hub.json`) | `<Fabric>/bin` |
|
|
52
52
|
|---|---|---|
|
|
53
|
-
| macOS | `~/Library/Application Support/ai.passioncode.fabric/` | `~/Applications/Fabric.app/Contents/Resources/bin`, then `/Applications` if yours or root's |
|
|
53
|
+
| macOS | `~/Library/Application Support/ai.passioncode.fabric/` | `$PASSIONCODE_APPLICATIONS/Fabric.app/...` if absolute, else `~/Applications/Fabric.app/Contents/Resources/bin`, then `/Applications` if yours or root's |
|
|
54
54
|
| Windows | `%LOCALAPPDATA%\passioncode-fabric\` | the install folder's `resources\bin` (`fabric-mcp.cmd`); per-user default `%LOCALAPPDATA%\Programs\Fabric` |
|
|
55
55
|
| Linux | `$XDG_DATA_HOME/passioncode-fabric/` (default `~/.local/share/`) | the install folder's `resources/bin`; the `.deb` puts it in `/opt/Fabric` |
|
|
56
56
|
|
|
@@ -88,7 +88,7 @@ claude mcp add --scope user fabric -- "<Fabric>/bin/fabric-mcp" --runner claude-
|
|
|
88
88
|
codex mcp add fabric -- "<Fabric>/bin/fabric-mcp" --runner codex
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
On a Mac, a registration naming another user's copy (outside your home,
|
|
91
|
+
On a Mac, a registration naming another user's copy (outside your home, not yours or root's) is replaced by your
|
|
92
92
|
own (or root's) copy: `claude mcp remove` then `add` with the same scope; a project's `.mcp.json` entry is overridden with
|
|
93
93
|
`--scope local`, never edited (`status` checks Claude Code's).
|
|
94
94
|
|
|
@@ -154,7 +154,7 @@ Fabric v0.3.5+ (REQ-38):
|
|
|
154
154
|
nothing either (Fabric chose that parent, FAA-153) and tell the person which. `spawned_from: null` comes
|
|
155
155
|
with `spawned_from_why`: link it yourself once with `fabric_task_link {taskId: <the piece>, rel: "spawned",
|
|
156
156
|
targetTaskId: <the claimed task>}`, and if that is refused too, tell the person that reason; never file the piece
|
|
157
|
-
again. A filed piece (`created: true`) with no `spawned_from`
|
|
157
|
+
again. A filed piece (`created: true`) with no `spawned_from` is a Fabric before 0.3.14: link it yourself the same way unless the answer says
|
|
158
158
|
`chained: true` with `chained_to` your claimed task (board `c42071bc`); an answer with `chained: false` names its
|
|
159
159
|
`chain_reason`, which you tell the person if your link is refused.
|
|
160
160
|
3. A related-work warning (same backlog row, a linked task, an overlapping folder) names the other agent, its
|
|
@@ -340,7 +340,7 @@ python3 <skill-dir>/scripts/fabric_session.py install-host-hooks --host codex --
|
|
|
340
340
|
It edits `~/.codex/hooks.json` (`hooks.SessionStart`) or appends one `[[hooks]]` table to `~/.kimi-code/config.toml`,
|
|
341
341
|
backs the file up first, refuses a file of another shape or one that changed since the dry run, and `--remove` takes
|
|
342
342
|
it out again. Hermes/OpenCode/OpenClaw/Pi use their native plugin loaders, not Claude hook JSON.
|
|
343
|
-
Hermes
|
|
343
|
+
Hermes needs `hermes plugins enable fabric-agent-adapter`; removal uses `hermes plugins disable fabric-agent-adapter`.
|
|
344
344
|
Read [native host hooks](references/native-host-hooks.md) for exact paths, permission behavior and verification.
|
|
345
345
|
Registration and a loaded hook do not prove admission. Never edit those files by hand. Without the hook, put the preamble into the first prompt
|
|
346
346
|
yourself: "Call `fabric_whoami` first; `com.status` and `com.list`, answering what waits this turn; `fabric_work_list`,
|
package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md
CHANGED
|
@@ -114,10 +114,12 @@ hook, or a Switchboard before 0.7.8 started the run, the launcher's appended exi
|
|
|
114
114
|
where CO-506 grants nothing (a machine marked as an organization's, no live credential for the session or its runner,
|
|
115
115
|
or a standing denial of that ask); the reply then names CO-506 though it shipped, and the real gate is that grant (left open in FAA-215).
|
|
116
116
|
FAA-219; Fabric's answer in board thread `1add3c03` (option (b), 2026-11-05), the responder's run's rule plus the
|
|
117
|
-
slot condition, which
|
|
117
|
+
slot condition, which Fabric's `board.read-at-start` took from the adapter (FAA-231, main `dbfbfce4`, in no release
|
|
118
|
+
yet);
|
|
118
119
|
Fabric's `com.enroll` description in 0.3.16 tells a session to ask the grant, which it cannot, and 0.3.16's own
|
|
119
|
-
`not_authorized` refusal says to reply with the answer; step 3 wins over both
|
|
120
|
-
|
|
120
|
+
`not_authorized` refusal says to reply with the answer; step 3 wins over both (Fabric's `main` gives `com.enroll`'s
|
|
121
|
+
tool description the same slot condition since `12d57bdd`, FAA-231, in no release yet); the refusal text waits
|
|
122
|
+
for its CO-507. An earlier run may have left the request taken: `com.get` on the
|
|
121
123
|
request shows its state, and the latest checkpoint (step 1) what that run recorded. An accepted request is never
|
|
122
124
|
claimed again: take it over with `com.responder_replace {expectedGeneration, transfer}` (its receipt carries the new
|
|
123
125
|
attempt). An `outcome_unknown` one is resolved only by `com.reconcile` with an observed result; so is an `in_progress` one an
|
|
@@ -195,8 +195,9 @@ operator once with `fabric_question_ask` (`kind: access`, `about: board.gate.<ca
|
|
|
195
195
|
`python3 <skill-dir>/scripts/fabric_session.py gate-id <request>` prints, the same a run uses, and a `text` naming the request, the gate and what
|
|
196
196
|
opens it; without Python, no `commandId` can be made: tell the person only), tells the person (the question stays
|
|
197
197
|
filed even when that is the operator: his decision is recorded under that `about` in Fabric), and stops handling that
|
|
198
|
-
request without waiting (Fabric's `board.read-at-start` rule
|
|
199
|
-
|
|
198
|
+
request without waiting (Fabric's `board.read-at-start` rule says the same, slot condition included, from its
|
|
199
|
+
CO-517 and the agreement on FAA-231, main `603da12f` and `dbfbfce4`, and so does `com.enroll`'s own tool description
|
|
200
|
+
since main `12d57bdd`; in no release yet: on Fabric 0.3.16 this rule wins over both). Never write "it stays queued" in a reply: the reply itself ends the queue. A
|
|
200
201
|
run Fabric started on a board request has no person in its chat to tell: with the slot `absent` at generation 0 it declines,
|
|
201
202
|
asks for a resend and asks the operator with `fabric_question_ask`; otherwise it leaves the request queued ([on-demand session](on-demand-session.md), step 3).
|
|
202
203
|
|
|
@@ -48,9 +48,12 @@ from fabric_session_env import ( # noqa: F401
|
|
|
48
48
|
_home,
|
|
49
49
|
fabric_root,
|
|
50
50
|
state_root,
|
|
51
|
+
a_file,
|
|
52
|
+
administrators_copy,
|
|
51
53
|
another_users_copy,
|
|
52
54
|
fabric_bin_candidates,
|
|
53
55
|
find_fabric_bin,
|
|
56
|
+
launcher_folder,
|
|
54
57
|
read_small,
|
|
55
58
|
pid_alive,
|
|
56
59
|
locate_hub,
|
package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py
CHANGED
|
@@ -25,11 +25,15 @@ from fabric_session_env import ( # noqa: E402 - the kit (FAA-65)
|
|
|
25
25
|
_write_private,
|
|
26
26
|
claude_registration,
|
|
27
27
|
codex_registered,
|
|
28
|
+
a_file,
|
|
29
|
+
administrators_copy,
|
|
28
30
|
another_users_copy,
|
|
29
31
|
fabric_bin_candidates,
|
|
30
32
|
fabric_launched,
|
|
31
33
|
fabric_root,
|
|
34
|
+
APPLICATIONS_ENV,
|
|
32
35
|
find_fabric_bin,
|
|
36
|
+
launcher_folder,
|
|
33
37
|
host_registered,
|
|
34
38
|
launched_by_ancestor,
|
|
35
39
|
locate_hub,
|
|
@@ -722,14 +726,29 @@ def status(cwd: Path, env: Optional[Dict[str, str]] = None, platform: str = sys.
|
|
|
722
726
|
foreign = bool(claude) and claude["fabricMcp"] and not command.startswith(("./", ".\\")) and another_users_copy(
|
|
723
727
|
Path(command), platform, env)
|
|
724
728
|
report["ownCopyRule"] = platform == "darwin"
|
|
729
|
+
chosen = launcher_folder(env) if platform == "darwin" else None
|
|
730
|
+
report["launcherFolder"] = str(chosen) if chosen else None
|
|
731
|
+
# FAA-237: root's copy runs, and the launcher calls it the administrator's and does not update it
|
|
732
|
+
report["administratorsCopy"] = bool(binary) and administrators_copy(binary, platform, env)
|
|
725
733
|
report["registeredCopy"] = "another user's" if foreign else "ok"
|
|
734
|
+
# where the user's own copy goes: the launcher's folder when the person named one, else ~/Applications (FAA-237);
|
|
735
|
+
# a launcher's folder that already holds another user's Fabric.app is no place for this user's (0.70.0 seam reading
|
|
736
|
+
# 3): unset PASSIONCODE_APPLICATIONS and use ~/Applications, as the launcher's PR #277 says then (its 0.2.75 would
|
|
737
|
+
# install into /Applications with the variable unset, so the person puts the copy there)
|
|
738
|
+
candidates = fabric_bin_candidates(platform, env)
|
|
739
|
+
occupied = bool(chosen) and a_file(candidates[0]) and another_users_copy(candidates[0], platform, env)
|
|
740
|
+
place = str(chosen) if chosen and not occupied else "~/Applications"
|
|
741
|
+
if occupied:
|
|
742
|
+
unset = {k: v for k, v in (os.environ if env is None else env).items() if k != APPLICATIONS_ENV}
|
|
743
|
+
candidates = fabric_bin_candidates(platform, unset)
|
|
744
|
+
place = "~/Applications, with %s unset: %s holds another user's Fabric.app" % (APPLICATIONS_ENV, chosen)
|
|
726
745
|
if binary is None and claude is None:
|
|
727
746
|
report["next"] = "Install Fabric v0.3.5 or later%s; no fabric-mcp %sis at %s." % (
|
|
728
|
-
" (your own copy, in
|
|
747
|
+
" (your own copy, in %s)" % place if platform == "darwin" else "", "of yours " if platform == "darwin" else "",
|
|
729
748
|
", ".join(str(p) for p in fabric_bin_candidates(platform, env)))
|
|
730
749
|
elif foreign:
|
|
731
|
-
own = '"%s"' % (binary or
|
|
732
|
-
first = "" if binary else "put your own Fabric.app in
|
|
750
|
+
own = '"%s"' % (binary or candidates[0])
|
|
751
|
+
first = "" if binary else "put your own Fabric.app in %s, then " % place
|
|
733
752
|
where = claude.get("folder", "")
|
|
734
753
|
if claude["scope"] == "project":
|
|
735
754
|
# the project's .mcp.json is shared with every collaborator: a local entry overrides it for this user alone
|
|
@@ -760,8 +779,10 @@ def print_status(report: Dict[str, Any]) -> None:
|
|
|
760
779
|
"not-a-hub": "not verified: %s" % hub.get("reason")}[hub["state"]]
|
|
761
780
|
claude = report["claudeCode"]
|
|
762
781
|
print("Fabric: %s" % line)
|
|
763
|
-
print("fabric-mcp: %s" % (report["fabricMcp"]
|
|
764
|
-
|
|
782
|
+
print("fabric-mcp: %s" % ((report["fabricMcp"] + (" (the administrator's copy, which the launcher does not update)"
|
|
783
|
+
if report.get("administratorsCopy") else "")) if report["fabricMcp"] else (
|
|
784
|
+
"none of yours in %s (PASSIONCODE_APPLICATIONS)" % report["launcherFolder"] if report.get("launcherFolder")
|
|
785
|
+
else "none of yours at the default install locations" if report.get("ownCopyRule")
|
|
765
786
|
else "not found at the default install locations")))
|
|
766
787
|
print("Claude Code: %s" % ("fabric registered (%s scope) -> %s%s" % (
|
|
767
788
|
claude["scope"], claude["command"], "" if claude["fabricMcp"] else " (not fabric-mcp)") + (
|
package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py
CHANGED
|
@@ -58,7 +58,7 @@ SESSION_ID = re.compile(r"[A-Za-z0-9._:-]{1,128}")
|
|
|
58
58
|
LOOPBACK_ORIGIN = re.compile(r"http://127\.0\.0\.1:(\d{1,5})")
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
VERSION = "0.
|
|
61
|
+
VERSION = "0.70.0"
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
HOSTS = {"claude": "Claude Code", "codex": "Codex", "kimi": "Kimi Code",
|
|
@@ -162,11 +162,29 @@ def state_root(platform: str = sys.platform, env: Optional[Dict[str, str]] = Non
|
|
|
162
162
|
return Path(env.get("XDG_CACHE_HOME") or str(home / ".cache")) / "fabric-agent-adapter"
|
|
163
163
|
|
|
164
164
|
|
|
165
|
+
#: The launcher's folder for every app it installs, when the person sets one (FAA-237).
|
|
166
|
+
APPLICATIONS_ENV = "PASSIONCODE_APPLICATIONS"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def launcher_folder(env: Optional[Dict[str, str]] = None) -> Optional[Path]:
|
|
170
|
+
"""The folder `PASSIONCODE_APPLICATIONS` names, when it starts with `/` (FAA-237). The launcher's `appsDir` takes
|
|
171
|
+
any non-empty value as it is, so a relative value or a literal `~` would be a folder under the launcher's working
|
|
172
|
+
folder, which no other process can find: none here, and the default order holds. A macOS path is POSIX on
|
|
173
|
+
whatever host reads it."""
|
|
174
|
+
value = (os.environ if env is None else env).get(APPLICATIONS_ENV) or ""
|
|
175
|
+
return Path(value) if value.startswith("/") else None
|
|
176
|
+
|
|
177
|
+
|
|
165
178
|
def fabric_bin_candidates(platform: str = sys.platform, env: Optional[Dict[str, str]] = None) -> List[Path]:
|
|
166
179
|
"""Where `<Fabric>/bin/fabric-mcp` is on a default install (ADR-0134 amendment 2; electron-builder targets)."""
|
|
167
180
|
env = os.environ if env is None else env
|
|
168
181
|
home = _home(env)
|
|
169
182
|
if platform == "darwin":
|
|
183
|
+
# The launcher's one folder for every app, when the person set it (its lib/catalog.js appsDir; FAA-237): only
|
|
184
|
+
# there. An empty or relative value is not a place, and the default order holds.
|
|
185
|
+
chosen = launcher_folder(env)
|
|
186
|
+
if chosen:
|
|
187
|
+
return [chosen / "Fabric.app/Contents/Resources/bin/fabric-mcp"]
|
|
170
188
|
# The user's own copy first: on a Mac with several users a copy in /Applications may be another user's
|
|
171
189
|
# (FAA-229; the launcher's order from its PR #277, lib/catalog.js appPath, planned for launcher 0.2.76).
|
|
172
190
|
return [home / "Applications/Fabric.app/Contents/Resources/bin/fabric-mcp",
|
|
@@ -184,6 +202,15 @@ def _owner_uid(path: Path) -> Optional[int]:
|
|
|
184
202
|
return None
|
|
185
203
|
|
|
186
204
|
|
|
205
|
+
def a_file(path: Path) -> bool:
|
|
206
|
+
"""`path.is_file()`, False where it cannot be read: the launcher's folder is the person's choice and may sit under a
|
|
207
|
+
folder this user cannot enter, where Python before 3.14 raises PermissionError (0.70.0 unit reading 4)."""
|
|
208
|
+
try:
|
|
209
|
+
return path.is_file()
|
|
210
|
+
except (OSError, ValueError):
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
|
|
187
214
|
def _real(path: Path) -> Path:
|
|
188
215
|
try:
|
|
189
216
|
return Path(os.path.realpath(str(path)))
|
|
@@ -215,13 +242,24 @@ def another_users_copy(path: Path, platform: str = sys.platform, env: Optional[D
|
|
|
215
242
|
return owner is not None and owner not in (os.getuid(), 0)
|
|
216
243
|
|
|
217
244
|
|
|
245
|
+
def administrators_copy(path: Path, platform: str = sys.platform, env: Optional[Dict[str, str]] = None) -> bool:
|
|
246
|
+
"""On macOS, a file outside the user's home that root owns: an install the administrator made (a pkg or MDM). This
|
|
247
|
+
user may run it, and the launcher does not update it (its doctor calls it the administrator's from its PR #277,
|
|
248
|
+
planned for 0.2.76; FAA-237)."""
|
|
249
|
+
if platform != "darwin" or not hasattr(os, "getuid") or os.getuid() == 0 or not path.is_absolute():
|
|
250
|
+
return False
|
|
251
|
+
path = _real(path)
|
|
252
|
+
if _real(_home(os.environ if env is None else env)) in path.parents:
|
|
253
|
+
return False
|
|
254
|
+
return _owner_uid(path) == 0
|
|
255
|
+
|
|
256
|
+
|
|
218
257
|
def find_fabric_bin(platform: str = sys.platform, env: Optional[Dict[str, str]] = None) -> Optional[Path]:
|
|
219
|
-
"""The first candidate that is a file
|
|
220
|
-
user's
|
|
221
|
-
|
|
222
|
-
appPath, from its PR #277, planned for 0.2.76): it updates the bundle, while this only runs it."""
|
|
258
|
+
"""The first candidate that is a file (only the launcher's folder when `PASSIONCODE_APPLICATIONS` names one). On
|
|
259
|
+
macOS a copy outside the user's home is taken only when it is this user's or root's (an install every user
|
|
260
|
+
shares): another user's copy is skipped (FAA-229, FAA-237)."""
|
|
223
261
|
for candidate in fabric_bin_candidates(platform, env):
|
|
224
|
-
if candidate
|
|
262
|
+
if a_file(candidate) and not another_users_copy(candidate, platform, env):
|
|
225
263
|
return candidate
|
|
226
264
|
return None
|
|
227
265
|
|