@uniqbit/mate-core 0.17.2-canary.3 → 0.18.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.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/src/cli/commands/companion/update.ts +43 -6
  3. package/src/cli/commands/install.ts +3 -1
  4. package/src/cli/commands/plugin/plugin.ts +1 -1
  5. package/src/cli/commands/plugin/verify.ts +4 -8
  6. package/src/cli/commands/studio/terminal-launch.ts +1 -51
  7. package/src/cli/commands/studio/terminal.ts +2 -13
  8. package/src/lib/install.ts +3 -0
  9. package/src/lib/orchestrator/types.ts +0 -6
  10. package/src/templates/mate-skills/agents/mate-domain-modeling/SKILL.md +5 -2
  11. package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +5 -2
  12. package/src/templates/mate-skills/agents/mate-grill-with-docs/SKILL.md +5 -2
  13. package/src/templates/mate-skills/agents/mate-grilling/SKILL.md +5 -2
  14. package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -2
  15. package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +5 -2
  16. package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +5 -2
  17. package/src/templates/mate-skills/claude/mate-domain-modeling/SKILL.md +5 -2
  18. package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +5 -2
  19. package/src/templates/mate-skills/claude/mate-grill-with-docs/SKILL.md +5 -2
  20. package/src/templates/mate-skills/claude/mate-grilling/SKILL.md +5 -2
  21. package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -2
  22. package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +5 -2
  23. package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +5 -2
  24. package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +90 -63
  25. package/src/tools/setup/capabilities/openspec.ts +55 -27
  26. package/src/tools/setup/capabilities/react-doctor.ts +8 -16
  27. package/src/tools/setup/capabilities/tokensave.ts +1 -0
  28. package/src/tools/setup/dynamic-plugins/frozen.ts +24 -3
  29. package/src/tools/setup/dynamic-plugins/loader.ts +16 -0
  30. package/src/tools/setup/dynamic-plugins/policy.ts +55 -0
  31. package/src/tools/setup/dynamic-plugins/staging.ts +11 -5
  32. package/src/tools/setup/dynamic-plugins/verify.ts +22 -18
  33. package/src/tools/setup/engine.ts +6 -1
  34. package/src/tools/setup/install-contract.ts +2 -0
  35. package/src/tools/setup/mate.ts +3 -1
  36. package/src/tools/setup/plugin.ts +2 -0
  37. package/src/tools/setup/providers/agents-skill-root.ts +79 -0
  38. package/src/tools/setup/providers/opencode.ts +8 -9
@@ -70,6 +70,7 @@ Write chunks to $GRAPHIFY_OUT and read $GRAPHIFY_OUT later.
70
70
  "opencodePlugins": {},
71
71
  "opencodeSkills": {},
72
72
  "opencodeTui": null,
73
+ "sharedSkills": {},
73
74
  }
74
75
  `;
75
76
 
@@ -277,6 +278,7 @@ Write chunks to $GRAPHIFY_OUT and read $GRAPHIFY_OUT later.
277
278
  ,
278
279
  },
279
280
  "opencodeTui": null,
281
+ "sharedSkills": {},
280
282
  }
281
283
  `;
282
284
 
@@ -505,6 +507,7 @@ Write chunks to $GRAPHIFY_OUT and read $GRAPHIFY_OUT later.
505
507
  ,
506
508
  },
507
509
  "opencodeTui": null,
510
+ "sharedSkills": {},
508
511
  }
509
512
  `;
510
513
 
@@ -841,6 +844,7 @@ exports[`runtime surface golden fixtures setup tokensave × claude 1`] = `
841
844
  "opencodePlugins": {},
842
845
  "opencodeSkills": {},
843
846
  "opencodeTui": null,
847
+ "sharedSkills": {},
844
848
  }
845
849
  `;
846
850
 
@@ -1054,6 +1058,7 @@ exports[`runtime surface golden fixtures setup tokensave × opencode 1`] = `
1054
1058
  "opencodePlugins": {},
1055
1059
  "opencodeSkills": {},
1056
1060
  "opencodeTui": null,
1061
+ "sharedSkills": {},
1057
1062
  }
1058
1063
  `;
1059
1064
 
@@ -1304,6 +1309,7 @@ exports[`runtime surface golden fixtures setup tokensave × claude+opencode 1`]
1304
1309
  "opencodePlugins": {},
1305
1310
  "opencodeSkills": {},
1306
1311
  "opencodeTui": null,
1312
+ "sharedSkills": {},
1307
1313
  }
1308
1314
  `;
1309
1315
 
@@ -1621,6 +1627,7 @@ exports[`runtime surface golden fixtures setup context-mode × claude 1`] = `
1621
1627
  "opencodePlugins": {},
1622
1628
  "opencodeSkills": {},
1623
1629
  "opencodeTui": null,
1630
+ "sharedSkills": {},
1624
1631
  }
1625
1632
  `;
1626
1633
 
@@ -1805,6 +1812,7 @@ Canonical Mate policy and any more specific tool-routing instructions remain aut
1805
1812
  "opencodePlugins": {},
1806
1813
  "opencodeSkills": {},
1807
1814
  "opencodeTui": null,
1815
+ "sharedSkills": {},
1808
1816
  }
1809
1817
  `;
1810
1818
 
@@ -1979,6 +1987,7 @@ Canonical Mate policy and any more specific tool-routing instructions remain aut
1979
1987
  "opencodePlugins": {},
1980
1988
  "opencodeSkills": {},
1981
1989
  "opencodeTui": null,
1990
+ "sharedSkills": {},
1982
1991
  }
1983
1992
  `;
1984
1993
 
@@ -2276,6 +2285,7 @@ exports[`runtime surface golden fixtures setup react-doctor × claude 1`] = `
2276
2285
  "opencodePlugins": {},
2277
2286
  "opencodeSkills": {},
2278
2287
  "opencodeTui": null,
2288
+ "sharedSkills": {},
2279
2289
  }
2280
2290
  `;
2281
2291
 
@@ -2448,11 +2458,12 @@ exports[`runtime surface golden fixtures setup react-doctor × opencode 1`] = `
2448
2458
  },
2449
2459
  },
2450
2460
  "opencodePlugins": {},
2451
- "opencodeSkills": {
2461
+ "opencodeSkills": {},
2462
+ "opencodeTui": null,
2463
+ "sharedSkills": {
2452
2464
  "react-doctor/SKILL.md": "df58b3490543",
2453
2465
  "react-doctor/references/explain.md": "2382ea820f20",
2454
2466
  },
2455
- "opencodeTui": null,
2456
2467
  }
2457
2468
  `;
2458
2469
 
@@ -2647,11 +2658,12 @@ exports[`runtime surface golden fixtures setup react-doctor × claude+opencode 1
2647
2658
  },
2648
2659
  },
2649
2660
  "opencodePlugins": {},
2650
- "opencodeSkills": {
2661
+ "opencodeSkills": {},
2662
+ "opencodeTui": null,
2663
+ "sharedSkills": {
2651
2664
  "react-doctor/SKILL.md": "df58b3490543",
2652
2665
  "react-doctor/references/explain.md": "2382ea820f20",
2653
2666
  },
2654
- "opencodeTui": null,
2655
2667
  }
2656
2668
  `;
2657
2669
 
@@ -2930,16 +2942,16 @@ exports[`runtime surface golden fixtures setup openspec × claude 1`] = `
2930
2942
  "mate-artifact-publish/SKILL.md": "9ae069fc6d0d",
2931
2943
  "mate-artifact-publish/references/openspec.md": "b5c986ed9bdc",
2932
2944
  "mate-create-report/SKILL.md": "4dda64fab7b7",
2933
- "mate-domain-modeling/SKILL.md": "22f1d92b65ce",
2945
+ "mate-domain-modeling/SKILL.md": "e9b812c5b70f",
2934
2946
  "mate-domain-modeling/references/ADR-FORMAT.md": "824b87e87d9b",
2935
2947
  "mate-domain-modeling/references/CONTEXT-FORMAT.md": "172622594277",
2936
- "mate-grill-me/SKILL.md": "bbccd2654f71",
2937
- "mate-grill-with-docs/SKILL.md": "d79278237d49",
2938
- "mate-grilling/SKILL.md": "9fa3fb50cc73",
2939
- "mate-interview-me/SKILL.md": "ad5d76230917",
2948
+ "mate-grill-me/SKILL.md": "c6e7bba6ec12",
2949
+ "mate-grill-with-docs/SKILL.md": "2e4dbb346e51",
2950
+ "mate-grilling/SKILL.md": "168a259e69ae",
2951
+ "mate-interview-me/SKILL.md": "79f21214a92d",
2940
2952
  "mate-openspec-backfill/SKILL.md": "fa73daee9951",
2941
- "mate-show-me/SKILL.md": "3ecf5d44eb45",
2942
- "mate-simplify-code/SKILL.md": "5dd2abc9571b",
2953
+ "mate-show-me/SKILL.md": "7ccd84f967cf",
2954
+ "mate-simplify-code/SKILL.md": "6fff7fff920c",
2943
2955
  "openspec-apply-change/SKILL.md": "b023edd228ab",
2944
2956
  "openspec-archive-change/SKILL.md": "890bada99035",
2945
2957
  "openspec-explore/SKILL.md": "477fdec2cdb0",
@@ -2949,6 +2961,7 @@ exports[`runtime surface golden fixtures setup openspec × claude 1`] = `
2949
2961
  "opencodePlugins": {},
2950
2962
  "opencodeSkills": {},
2951
2963
  "opencodeTui": null,
2964
+ "sharedSkills": {},
2952
2965
  }
2953
2966
  `;
2954
2967
 
@@ -3136,26 +3149,28 @@ exports[`runtime surface golden fixtures setup openspec × opencode 1`] = `
3136
3149
  },
3137
3150
  },
3138
3151
  "opencodePlugins": {},
3139
- "opencodeSkills": {
3152
+ "opencodeSkills": {},
3153
+ "opencodeTui": null,
3154
+ "sharedSkills": {
3155
+ ".openspec-target": "38700dfad571",
3140
3156
  "mate-artifact-publish/SKILL.md": "9ae069fc6d0d",
3141
3157
  "mate-artifact-publish/references/openspec.md": "b5c986ed9bdc",
3142
3158
  "mate-create-report/SKILL.md": "4dda64fab7b7",
3143
- "mate-domain-modeling/SKILL.md": "22f1d92b65ce",
3159
+ "mate-domain-modeling/SKILL.md": "e9b812c5b70f",
3144
3160
  "mate-domain-modeling/references/ADR-FORMAT.md": "824b87e87d9b",
3145
3161
  "mate-domain-modeling/references/CONTEXT-FORMAT.md": "172622594277",
3146
- "mate-grill-me/SKILL.md": "bbccd2654f71",
3147
- "mate-grill-with-docs/SKILL.md": "d79278237d49",
3148
- "mate-grilling/SKILL.md": "9fa3fb50cc73",
3149
- "mate-interview-me/SKILL.md": "ad5d76230917",
3162
+ "mate-grill-me/SKILL.md": "c6e7bba6ec12",
3163
+ "mate-grill-with-docs/SKILL.md": "2e4dbb346e51",
3164
+ "mate-grilling/SKILL.md": "168a259e69ae",
3165
+ "mate-interview-me/SKILL.md": "79f21214a92d",
3150
3166
  "mate-openspec-backfill/SKILL.md": "fa73daee9951",
3151
- "mate-show-me/SKILL.md": "3ecf5d44eb45",
3152
- "mate-simplify-code/SKILL.md": "5dd2abc9571b",
3153
- "openspec-apply-change/SKILL.md": "cb6e43b175c8",
3154
- "openspec-archive-change/SKILL.md": "920c4cc68edf",
3155
- "openspec-explore/SKILL.md": "87d1237e5ffa",
3156
- "openspec-propose/SKILL.md": "3618b84ce74c",
3167
+ "mate-show-me/SKILL.md": "7ccd84f967cf",
3168
+ "mate-simplify-code/SKILL.md": "6fff7fff920c",
3169
+ "openspec-apply-change/SKILL.md": "c657f1b4e9eb",
3170
+ "openspec-archive-change/SKILL.md": "ad2463670c0f",
3171
+ "openspec-explore/SKILL.md": "214bba092ffa",
3172
+ "openspec-propose/SKILL.md": "c09159964f8e",
3157
3173
  },
3158
- "opencodeTui": null,
3159
3174
  }
3160
3175
  `;
3161
3176
 
@@ -3317,16 +3332,16 @@ exports[`runtime surface golden fixtures setup openspec × claude+opencode 1`] =
3317
3332
  "mate-artifact-publish/SKILL.md": "9ae069fc6d0d",
3318
3333
  "mate-artifact-publish/references/openspec.md": "b5c986ed9bdc",
3319
3334
  "mate-create-report/SKILL.md": "4dda64fab7b7",
3320
- "mate-domain-modeling/SKILL.md": "22f1d92b65ce",
3335
+ "mate-domain-modeling/SKILL.md": "e9b812c5b70f",
3321
3336
  "mate-domain-modeling/references/ADR-FORMAT.md": "824b87e87d9b",
3322
3337
  "mate-domain-modeling/references/CONTEXT-FORMAT.md": "172622594277",
3323
- "mate-grill-me/SKILL.md": "bbccd2654f71",
3324
- "mate-grill-with-docs/SKILL.md": "d79278237d49",
3325
- "mate-grilling/SKILL.md": "9fa3fb50cc73",
3326
- "mate-interview-me/SKILL.md": "ad5d76230917",
3338
+ "mate-grill-me/SKILL.md": "c6e7bba6ec12",
3339
+ "mate-grill-with-docs/SKILL.md": "2e4dbb346e51",
3340
+ "mate-grilling/SKILL.md": "168a259e69ae",
3341
+ "mate-interview-me/SKILL.md": "79f21214a92d",
3327
3342
  "mate-openspec-backfill/SKILL.md": "fa73daee9951",
3328
- "mate-show-me/SKILL.md": "3ecf5d44eb45",
3329
- "mate-simplify-code/SKILL.md": "5dd2abc9571b",
3343
+ "mate-show-me/SKILL.md": "7ccd84f967cf",
3344
+ "mate-simplify-code/SKILL.md": "6fff7fff920c",
3330
3345
  "openspec-apply-change/SKILL.md": "b023edd228ab",
3331
3346
  "openspec-archive-change/SKILL.md": "890bada99035",
3332
3347
  "openspec-explore/SKILL.md": "477fdec2cdb0",
@@ -3353,26 +3368,28 @@ exports[`runtime surface golden fixtures setup openspec × claude+opencode 1`] =
3353
3368
  },
3354
3369
  },
3355
3370
  "opencodePlugins": {},
3356
- "opencodeSkills": {
3371
+ "opencodeSkills": {},
3372
+ "opencodeTui": null,
3373
+ "sharedSkills": {
3374
+ ".openspec-target": "38700dfad571",
3357
3375
  "mate-artifact-publish/SKILL.md": "9ae069fc6d0d",
3358
3376
  "mate-artifact-publish/references/openspec.md": "b5c986ed9bdc",
3359
3377
  "mate-create-report/SKILL.md": "4dda64fab7b7",
3360
- "mate-domain-modeling/SKILL.md": "22f1d92b65ce",
3378
+ "mate-domain-modeling/SKILL.md": "e9b812c5b70f",
3361
3379
  "mate-domain-modeling/references/ADR-FORMAT.md": "824b87e87d9b",
3362
3380
  "mate-domain-modeling/references/CONTEXT-FORMAT.md": "172622594277",
3363
- "mate-grill-me/SKILL.md": "bbccd2654f71",
3364
- "mate-grill-with-docs/SKILL.md": "d79278237d49",
3365
- "mate-grilling/SKILL.md": "9fa3fb50cc73",
3366
- "mate-interview-me/SKILL.md": "ad5d76230917",
3381
+ "mate-grill-me/SKILL.md": "c6e7bba6ec12",
3382
+ "mate-grill-with-docs/SKILL.md": "2e4dbb346e51",
3383
+ "mate-grilling/SKILL.md": "168a259e69ae",
3384
+ "mate-interview-me/SKILL.md": "79f21214a92d",
3367
3385
  "mate-openspec-backfill/SKILL.md": "fa73daee9951",
3368
- "mate-show-me/SKILL.md": "3ecf5d44eb45",
3369
- "mate-simplify-code/SKILL.md": "5dd2abc9571b",
3370
- "openspec-apply-change/SKILL.md": "cb6e43b175c8",
3371
- "openspec-archive-change/SKILL.md": "920c4cc68edf",
3372
- "openspec-explore/SKILL.md": "87d1237e5ffa",
3373
- "openspec-propose/SKILL.md": "3618b84ce74c",
3386
+ "mate-show-me/SKILL.md": "7ccd84f967cf",
3387
+ "mate-simplify-code/SKILL.md": "6fff7fff920c",
3388
+ "openspec-apply-change/SKILL.md": "c657f1b4e9eb",
3389
+ "openspec-archive-change/SKILL.md": "ad2463670c0f",
3390
+ "openspec-explore/SKILL.md": "214bba092ffa",
3391
+ "openspec-propose/SKILL.md": "c09159964f8e",
3374
3392
  },
3375
- "opencodeTui": null,
3376
3393
  }
3377
3394
  `;
3378
3395
 
@@ -3782,16 +3799,16 @@ Write chunks to $GRAPHIFY_OUT and read $GRAPHIFY_OUT later.
3782
3799
  "mate-artifact-publish/SKILL.md": "9ae069fc6d0d",
3783
3800
  "mate-artifact-publish/references/openspec.md": "b5c986ed9bdc",
3784
3801
  "mate-create-report/SKILL.md": "4dda64fab7b7",
3785
- "mate-domain-modeling/SKILL.md": "22f1d92b65ce",
3802
+ "mate-domain-modeling/SKILL.md": "e9b812c5b70f",
3786
3803
  "mate-domain-modeling/references/ADR-FORMAT.md": "824b87e87d9b",
3787
3804
  "mate-domain-modeling/references/CONTEXT-FORMAT.md": "172622594277",
3788
- "mate-grill-me/SKILL.md": "bbccd2654f71",
3789
- "mate-grill-with-docs/SKILL.md": "d79278237d49",
3790
- "mate-grilling/SKILL.md": "9fa3fb50cc73",
3791
- "mate-interview-me/SKILL.md": "ad5d76230917",
3805
+ "mate-grill-me/SKILL.md": "c6e7bba6ec12",
3806
+ "mate-grill-with-docs/SKILL.md": "2e4dbb346e51",
3807
+ "mate-grilling/SKILL.md": "168a259e69ae",
3808
+ "mate-interview-me/SKILL.md": "79f21214a92d",
3792
3809
  "mate-openspec-backfill/SKILL.md": "fa73daee9951",
3793
- "mate-show-me/SKILL.md": "3ecf5d44eb45",
3794
- "mate-simplify-code/SKILL.md": "5dd2abc9571b",
3810
+ "mate-show-me/SKILL.md": "7ccd84f967cf",
3811
+ "mate-simplify-code/SKILL.md": "6fff7fff920c",
3795
3812
  "openspec-apply-change/SKILL.md": "b023edd228ab",
3796
3813
  "openspec-archive-change/SKILL.md": "890bada99035",
3797
3814
  "openspec-explore/SKILL.md": "477fdec2cdb0",
@@ -3854,27 +3871,30 @@ Write chunks to $GRAPHIFY_OUT and read $GRAPHIFY_OUT later.
3854
3871
  "Outputs land in $GRAPHIFY_OUT by default.
3855
3872
  "
3856
3873
  ,
3874
+ },
3875
+ "opencodeTui": null,
3876
+ "sharedSkills": {
3877
+ ".openspec-target": "38700dfad571",
3857
3878
  "mate-artifact-publish/SKILL.md": "9ae069fc6d0d",
3858
3879
  "mate-artifact-publish/references/openspec.md": "b5c986ed9bdc",
3859
3880
  "mate-create-report/SKILL.md": "4dda64fab7b7",
3860
- "mate-domain-modeling/SKILL.md": "22f1d92b65ce",
3881
+ "mate-domain-modeling/SKILL.md": "e9b812c5b70f",
3861
3882
  "mate-domain-modeling/references/ADR-FORMAT.md": "824b87e87d9b",
3862
3883
  "mate-domain-modeling/references/CONTEXT-FORMAT.md": "172622594277",
3863
- "mate-grill-me/SKILL.md": "bbccd2654f71",
3864
- "mate-grill-with-docs/SKILL.md": "d79278237d49",
3865
- "mate-grilling/SKILL.md": "9fa3fb50cc73",
3866
- "mate-interview-me/SKILL.md": "ad5d76230917",
3884
+ "mate-grill-me/SKILL.md": "c6e7bba6ec12",
3885
+ "mate-grill-with-docs/SKILL.md": "2e4dbb346e51",
3886
+ "mate-grilling/SKILL.md": "168a259e69ae",
3887
+ "mate-interview-me/SKILL.md": "79f21214a92d",
3867
3888
  "mate-openspec-backfill/SKILL.md": "fa73daee9951",
3868
- "mate-show-me/SKILL.md": "3ecf5d44eb45",
3869
- "mate-simplify-code/SKILL.md": "5dd2abc9571b",
3870
- "openspec-apply-change/SKILL.md": "cb6e43b175c8",
3871
- "openspec-archive-change/SKILL.md": "920c4cc68edf",
3872
- "openspec-explore/SKILL.md": "87d1237e5ffa",
3873
- "openspec-propose/SKILL.md": "3618b84ce74c",
3889
+ "mate-show-me/SKILL.md": "7ccd84f967cf",
3890
+ "mate-simplify-code/SKILL.md": "6fff7fff920c",
3891
+ "openspec-apply-change/SKILL.md": "c657f1b4e9eb",
3892
+ "openspec-archive-change/SKILL.md": "ad2463670c0f",
3893
+ "openspec-explore/SKILL.md": "214bba092ffa",
3894
+ "openspec-propose/SKILL.md": "c09159964f8e",
3874
3895
  "react-doctor/SKILL.md": "df58b3490543",
3875
3896
  "react-doctor/references/explain.md": "2382ea820f20",
3876
3897
  },
3877
- "opencodeTui": null,
3878
3898
  }
3879
3899
  `;
3880
3900
 
@@ -4246,6 +4266,7 @@ exports[`runtime surface golden fixtures teardown graphify × claude+opencode 1`
4246
4266
  "opencodePlugins": {},
4247
4267
  "opencodeSkills": {},
4248
4268
  "opencodeTui": null,
4269
+ "sharedSkills": {},
4249
4270
  }
4250
4271
  `;
4251
4272
 
@@ -4305,6 +4326,7 @@ exports[`runtime surface golden fixtures teardown tokensave × claude+opencode 1
4305
4326
  "opencodePlugins": {},
4306
4327
  "opencodeSkills": {},
4307
4328
  "opencodeTui": null,
4329
+ "sharedSkills": {},
4308
4330
  }
4309
4331
  `;
4310
4332
 
@@ -4365,6 +4387,7 @@ exports[`runtime surface golden fixtures teardown context-mode × claude+opencod
4365
4387
  "opencodePlugins": {},
4366
4388
  "opencodeSkills": {},
4367
4389
  "opencodeTui": null,
4390
+ "sharedSkills": {},
4368
4391
  }
4369
4392
  `;
4370
4393
 
@@ -4424,6 +4447,7 @@ exports[`runtime surface golden fixtures teardown react-doctor × claude+opencod
4424
4447
  "opencodePlugins": {},
4425
4448
  "opencodeSkills": {},
4426
4449
  "opencodeTui": null,
4450
+ "sharedSkills": {},
4427
4451
  }
4428
4452
  `;
4429
4453
 
@@ -4483,6 +4507,7 @@ exports[`runtime surface golden fixtures teardown openspec × claude+opencode 1`
4483
4507
  "opencodePlugins": {},
4484
4508
  "opencodeSkills": {},
4485
4509
  "opencodeTui": null,
4510
+ "sharedSkills": {},
4486
4511
  }
4487
4512
  `;
4488
4513
 
@@ -4556,6 +4581,7 @@ Write chunks to $GRAPHIFY_OUT and read $GRAPHIFY_OUT later.
4556
4581
  "opencodePlugins": {},
4557
4582
  "opencodeSkills": {},
4558
4583
  "opencodeTui": null,
4584
+ "sharedSkills": {},
4559
4585
  }
4560
4586
  `;
4561
4587
 
@@ -4693,5 +4719,6 @@ exports[`working repo golden fixtures hub roots set up providers only without co
4693
4719
  "opencodePlugins": {},
4694
4720
  "opencodeSkills": {},
4695
4721
  "opencodeTui": null,
4722
+ "sharedSkills": {},
4696
4723
  }
4697
4724
  `;
@@ -19,7 +19,13 @@ import {
19
19
  runCommand,
20
20
  runCommandSilently,
21
21
  } from "../utils";
22
- import { applyMateSkills, teardownMateSkills } from "../mate";
22
+ import { applyMateSkills, MANAGED_MATE_SKILL_NAMES, teardownMateSkills } from "../mate";
23
+ import {
24
+ getSharedSkillsDir,
25
+ isSharedSkillRootActive,
26
+ removeLegacyOpenCodeSkills,
27
+ removeSharedSkills,
28
+ } from "../providers/agents-skill-root";
23
29
 
24
30
  export { MATE_ARTIFACT_SKILLS, MATE_SKILLS } from "../mate";
25
31
 
@@ -30,10 +36,15 @@ export const OPENSPEC_SKILLS = [
30
36
  "openspec-propose",
31
37
  "openspec-apply-change",
32
38
  "openspec-archive-change",
39
+ "openspec-sync-specs",
40
+ "openspec-update-change",
33
41
  ] as const;
34
42
 
35
- // Mate-authored skills (not generated by the openspec CLI) are sourced from the
36
- // shared Mate skill tree and installed per active provider while OpenSpec is enabled.
43
+ /** Written by the OpenSpec `agents` tool beside its skills in the shared root. */
44
+ const OPENSPEC_AGENTS_MARKER = ".openspec-target";
45
+
46
+ /** Skill names the capability owns in any skill root. */
47
+ const MANAGED_SKILL_NAMES = [...OPENSPEC_SKILLS, ...MANAGED_MATE_SKILL_NAMES];
37
48
 
38
49
  /** Every Mate-owned schema is installed for availability; only one is ever activated as the companion default. */
39
50
  const MATE_SCHEMA_NAMES = ["mate-v1", "mate-minimal"] as const;
@@ -44,14 +55,19 @@ const MATE_SCHEMA_SOURCE_ROOT = path.join(
44
55
  );
45
56
  const MATE_CONVENTIONS_SOURCE = path.join(MATE_SCHEMA_SOURCE_ROOT, "openspec-conventions.yaml");
46
57
 
47
- const OPENSPEC_TOOL_DIRS = {
48
- claude: ".claude",
49
- opencode: ".opencode",
58
+ /**
59
+ * Runtime → OpenSpec CLI tools. OpenCode reads workflow skills from the shared
60
+ * `agents` root; its own tool only contributes the `/opsx-*` commands.
61
+ */
62
+ const OPENSPEC_TOOLS_BY_RUNTIME = {
63
+ claude: ["claude"],
64
+ opencode: ["agents", "opencode"],
50
65
  } as const;
51
66
 
52
67
  const OPENSPEC_INSTALL_COMMAND = "npm install -g @fission-ai/openspec@latest";
53
68
 
54
- type OpenSpecTool = keyof typeof OPENSPEC_TOOL_DIRS;
69
+ type OpenSpecTool =
70
+ (typeof OPENSPEC_TOOLS_BY_RUNTIME)[keyof typeof OPENSPEC_TOOLS_BY_RUNTIME][number];
55
71
  type OpenSpecRunCommand = typeof runCommandSilently;
56
72
 
57
73
  export interface OpenSpecPluginDeps {
@@ -66,8 +82,6 @@ export interface OpenSpecPluginDeps {
66
82
  isPinnedDeployment?: () => boolean;
67
83
  }
68
84
 
69
- const OPEN_SPEC_SUPPORTED_TOOLS = Object.keys(OPENSPEC_TOOL_DIRS) as OpenSpecTool[];
70
-
71
85
  async function defaultGetInstalledVersion(pathValue: string): Promise<string | undefined> {
72
86
  const bin = resolveCommandOnPath("openspec", pathValue);
73
87
  if (!bin) return undefined;
@@ -89,11 +103,14 @@ async function defaultFetchLatestVersion(): Promise<string | undefined> {
89
103
  }
90
104
 
91
105
  export function deriveOpenSpecTools(activeProviders: string[]): OpenSpecTool[] {
92
- return OPEN_SPEC_SUPPORTED_TOOLS.filter((tool) => activeProviders.includes(tool));
106
+ const active = new Set(activeProviders);
107
+ return Object.entries(OPENSPEC_TOOLS_BY_RUNTIME).flatMap(([runtime, tools]) =>
108
+ active.has(runtime) ? tools : [],
109
+ );
93
110
  }
94
111
 
95
- function getSkillsDir(companionPath: string, tool: OpenSpecTool): string {
96
- return path.join(companionPath, OPENSPEC_TOOL_DIRS[tool], "skills");
112
+ function getClaudeSkillsDir(companionPath: string): string {
113
+ return path.join(companionPath, ".claude", "skills");
97
114
  }
98
115
 
99
116
  function getOpenSpecConfigPath(companionPath: string): string {
@@ -108,7 +125,8 @@ function getMateConventionsPath(companionPath: string): string {
108
125
  return path.join(companionPath, "openspec", "mate-conventions.yaml");
109
126
  }
110
127
 
111
- async function teardownOpenspecSkills(skillsDir: string, companionPath: string): Promise<void> {
128
+ async function teardownClaudeSkillRoot(companionPath: string): Promise<void> {
129
+ const skillsDir = getClaudeSkillsDir(companionPath);
112
130
  for (const skill of OPENSPEC_SKILLS) {
113
131
  try {
114
132
  await fs.rm(path.join(skillsDir, skill), { recursive: true, force: true });
@@ -120,17 +138,24 @@ async function teardownOpenspecSkills(skillsDir: string, companionPath: string):
120
138
  await pruneEmptyAncestors(skillsDir, companionPath);
121
139
  }
122
140
 
123
- async function teardownToolRuntime(companionPath: string, tool: OpenSpecTool): Promise<void> {
124
- await teardownOpenspecSkills(getSkillsDir(companionPath, tool), companionPath);
141
+ async function teardownSharedSkillRoot(companionPath: string): Promise<void> {
142
+ await removeSharedSkills(companionPath, [...MANAGED_SKILL_NAMES, OPENSPEC_AGENTS_MARKER]);
143
+ }
144
+
145
+ /** Converges earlier layouts and drops the `opencode` tool's duplicate workflow skills. */
146
+ async function removeLegacyOpenCodeCopies(companionPath: string): Promise<void> {
147
+ await removeLegacyOpenCodeSkills(companionPath, MANAGED_SKILL_NAMES);
125
148
  }
126
149
 
127
150
  async function applyMateOpenspecSkills(
128
151
  companionPath: string,
129
- tools: OpenSpecTool[],
152
+ activeProviders: string[],
130
153
  ): Promise<void> {
131
- for (const tool of tools) {
132
- const skillsDir = getSkillsDir(companionPath, tool);
133
- await applyMateSkills(skillsDir, tool);
154
+ if (activeProviders.includes("claude")) {
155
+ await applyMateSkills(getClaudeSkillsDir(companionPath), "claude");
156
+ }
157
+ if (isSharedSkillRootActive(activeProviders)) {
158
+ await applyMateSkills(getSharedSkillsDir(companionPath), "agents");
134
159
  }
135
160
  }
136
161
 
@@ -158,9 +183,8 @@ async function reconcileOpenSpecTools(
158
183
  cwd: ctx.companionPath,
159
184
  });
160
185
 
161
- // The openspec CLI generates its own workflow skills above; layer the
162
- // mate-authored skills on top for the same active providers.
163
- await applyMateOpenspecSkills(ctx.companionPath, tools);
186
+ await removeLegacyOpenCodeCopies(ctx.companionPath);
187
+ await applyMateOpenspecSkills(ctx.companionPath, ctx.activeProviders);
164
188
  }
165
189
 
166
190
  async function updateOpenSpecIfOutdated(
@@ -393,12 +417,13 @@ export function createOpenspecPlugin(deps: OpenSpecPluginDeps = {}): CapabilityP
393
417
  }
394
418
  }
395
419
  }
420
+ await removeLegacyOpenCodeCopies(ctx.companionPath);
396
421
  await reconcileOpenSpecSchema(ctx);
397
422
  },
398
423
  async teardown(ctx) {
399
- for (const tool of OPEN_SPEC_SUPPORTED_TOOLS) {
400
- await teardownToolRuntime(ctx.companionPath, tool);
401
- }
424
+ await teardownClaudeSkillRoot(ctx.companionPath);
425
+ await teardownSharedSkillRoot(ctx.companionPath);
426
+ await removeLegacyOpenCodeCopies(ctx.companionPath);
402
427
  await teardownOpenSpecSchema(ctx.companionPath);
403
428
  },
404
429
  forProvider: {
@@ -407,13 +432,16 @@ export function createOpenspecPlugin(deps: OpenSpecPluginDeps = {}): CapabilityP
407
432
  // contributes no archive-triggered Claude hook and no hook state.
408
433
  async apply() {},
409
434
  async teardown(ctx: SetupContext) {
410
- await teardownToolRuntime(ctx.companionPath, "claude");
435
+ await teardownClaudeSkillRoot(ctx.companionPath);
411
436
  },
412
437
  },
413
438
  opencode: {
414
439
  async apply() {},
415
440
  async teardown(ctx: SetupContext) {
416
- await teardownToolRuntime(ctx.companionPath, "opencode");
441
+ if (!isSharedSkillRootActive(ctx.activeProviders)) {
442
+ await teardownSharedSkillRoot(ctx.companionPath);
443
+ }
444
+ await removeLegacyOpenCodeCopies(ctx.companionPath);
417
445
  },
418
446
  },
419
447
  },
@@ -2,8 +2,11 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
4
  import type { CapabilityPlugin, RuntimeContributionsByRuntime, SetupContext } from "../plugin";
5
+ import { removeLegacyOpenCodeSkills } from "../providers/agents-skill-root";
5
6
  import { pruneEmptyAncestors } from "../utils";
6
7
 
8
+ const SKILL_NAME = "react-doctor";
9
+
7
10
  const SKILL_SRC = path.join(
8
11
  import.meta.dirname,
9
12
  "../../../templates/capabilities/react-doctor/skill",
@@ -27,7 +30,7 @@ export function createReactDoctorPlugin(): CapabilityPlugin {
27
30
  // format the surface owns).
28
31
  getRuntimeContributions(ctx: SetupContext): RuntimeContributionsByRuntime {
29
32
  const hookCommand = `sh "${ctx.companionPath}/.claude/hooks/react-doctor.sh"`;
30
- const skillTrees = [{ name: "react-doctor", sourceDir: SKILL_SRC }];
33
+ const skillTrees = [{ name: SKILL_NAME, sourceDir: SKILL_SRC }];
31
34
  return {
32
35
  claude: {
33
36
  hookGroups: [
@@ -57,22 +60,11 @@ export function createReactDoctorPlugin(): CapabilityPlugin {
57
60
  };
58
61
  },
59
62
  async apply(ctx: SetupContext) {
60
- // Migrate: remove legacy shared .agents/skills/react-doctor/ from previous installs.
61
- try {
62
- await fs.rm(path.join(ctx.companionPath, ".agents", "skills", "react-doctor"), {
63
- recursive: true,
64
- force: true,
65
- });
66
- } catch {
67
- /* not present */
68
- }
69
- await pruneEmptyAncestors(
70
- path.join(ctx.companionPath, ".agents", "skills"),
71
- ctx.companionPath,
72
- );
73
- await pruneEmptyAncestors(path.join(ctx.companionPath, ".agents"), ctx.companionPath);
63
+ await removeLegacyOpenCodeSkills(ctx.companionPath, [SKILL_NAME]);
64
+ },
65
+ async teardown(ctx: SetupContext) {
66
+ await removeLegacyOpenCodeSkills(ctx.companionPath, [SKILL_NAME]);
74
67
  },
75
- async teardown() {},
76
68
  forProvider: {
77
69
  claude: {
78
70
  async apply(ctx: SetupContext) {
@@ -446,6 +446,7 @@ export function createTokensavePlugin(): CapabilityPlugin {
446
446
  await plan.run();
447
447
  },
448
448
  verify: () => tokensaveDeps.run(["--version"], process.cwd()).ok,
449
+ update: () => upgradeTokensave(process.cwd()),
449
450
  },
450
451
  ...(config.allowedAgents ?? [])
451
452
  .filter((agent) => TOKENSAVE_SUPPORTED_AGENTS.has(agent))
@@ -5,8 +5,14 @@ import path from "node:path";
5
5
  import type { PluginDeclaration } from "../../../lib/orchestrator/types";
6
6
  import type { NpmInstallRunner, PluginInstallResult } from "./install";
7
7
  import { dynamicPluginsWorkspaceRoot } from "./paths";
8
-
9
- /** Raised before anything is installed: manifest or lockfile inputs are unusable. */
8
+ import {
9
+ disallowedPluginMessage,
10
+ isPluginAllowed,
11
+ PluginPolicyError,
12
+ readPluginPolicy,
13
+ } from "./policy";
14
+
15
+ /** Raised before anything is installed: policy, manifest or lockfile inputs are unusable. */
10
16
  export class FrozenInstallError extends Error {}
11
17
 
12
18
  export interface FrozenInstallDeps {
@@ -106,7 +112,8 @@ async function treeMismatches(workspaceRoot: string, lock: Lockfile): Promise<st
106
112
 
107
113
  /**
108
114
  * Deployment restore: installs exactly what the committed lockfile records.
109
- * Inputs are validated before npm runs — the workspace manifest against the declarations, and the lockfile against the
115
+ * Inputs are validated before npm runs — the appliance allowlist, the
116
+ * workspace manifest against the declarations, and the lockfile against the
110
117
  * manifest — and neither tracked file is ever rewritten. An installed tree is
111
118
  * reused only when it matches the lockfile's versions and integrity.
112
119
  */
@@ -120,6 +127,20 @@ export async function installDeclaredPluginsFrozen(
120
127
  const desired: Record<string, string> = {};
121
128
  for (const declaration of sorted) desired[declaration.package] = declaration.version;
122
129
 
130
+ let policy: ReturnType<typeof readPluginPolicy>;
131
+ try {
132
+ policy = readPluginPolicy(deps.env ?? process.env);
133
+ } catch (error) {
134
+ if (error instanceof PluginPolicyError) throw new FrozenInstallError(error.message);
135
+ throw error;
136
+ }
137
+ const refused = sorted.filter((declaration) => !isPluginAllowed(policy, declaration.package));
138
+ if (refused.length > 0) {
139
+ throw new FrozenInstallError(
140
+ refused.map((declaration) => disallowedPluginMessage(declaration.package)).join("\n"),
141
+ );
142
+ }
143
+
123
144
  const manifestFile = path.join(workspaceRoot, "package.json");
124
145
  const lockFile = path.join(workspaceRoot, "package-lock.json");
125
146
  const [manifestText, lockText] = await Promise.all([readText(manifestFile), readText(lockFile)]);