kennel 0.2.12 → 0.2.13
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/dist/cli/src/plugins/external.js +19 -7
- package/dist/cli/src/site/server-plugins.d.ts +1 -4
- package/dist/core/api-client.js +3 -11
- package/dist/core/runtime.js +84 -695
- package/dist/package/preview.js +99 -729
- package/dist/plugins/external.js +19 -7
- package/dist/site/server-plugins.d.ts +1 -4
- package/dist/site/server-plugins.js +28 -537
- package/kernel/apps/web/package.json +0 -1
- package/kernel/apps/web/src/app.css +0 -1
- package/kernel/apps/web/src/app.tsx +7 -24
- package/kernel/apps/web/src/folder-plugin-view.tsx +6 -3
- package/kernel/apps/web/src/plugins.ts +2 -14
- package/kernel/apps/web/vite.config.ts +16 -3
- package/kernel/packages/api/openapi.json +85 -224
- package/kernel/packages/api/package.json +0 -1
- package/kernel/packages/api/src/contracts/schemas.ts +1 -9
- package/kernel/packages/api/src/server/folder-metadata-plugin.ts +9 -151
- package/kernel/packages/api/src/server/plugins.ts +11 -26
- package/kernel/packages/api/src/shared/viewer-route.ts +3 -5
- package/kernel/packages/api-client/src/generated.d.ts +16 -53
- package/kernel/packages/markdown/README.md +2 -2
- package/kernel/packages/notes/package.json +1 -0
- package/kernel/packages/notes/src/client.tsx +0 -1
- package/kernel/packages/notes/src/manifest.ts +7 -0
- package/kernel/packages/notes/src/pages/notes-page.tsx +1 -1
- package/kernel/packages/notes/src/ui/folder-notes-list.tsx +52 -7
- package/kernel/packages/plugins/SKILL.md +3 -2
- package/kernel/packages/plugins/package.json +5 -1
- package/kernel/packages/plugins/src/hooks.ts +24 -0
- package/kernel/packages/plugins/src/index.ts +1 -7
- package/kernel/packages/plugins/src/page.ts +0 -7
- package/kernel/packages/plugins/src/types.ts +8 -0
- package/kernel/packages/plugins/tsconfig.json +1 -1
- package/kernel/packages/plugins/tsup.config.ts +1 -0
- package/kernel/packages/ui/src/components/folder-activity.tsx +16 -98
- package/kernel/packages/ui/src/components/folder-landing.tsx +4 -0
- package/kernel/packages/ui/src/components/pagination.tsx +88 -2
- package/kernel/packages/ui/src/components/plugin-icon.tsx +19 -0
- package/kernel/packages/ui/src/lib/folder-activity.ts +4 -17
- package/kernel/packages/ui/src/styles/folder-activity.css +1 -31
- package/package.json +2 -2
- package/web/assets/{abnfDiagram-VRR7QNED-Brm1vcGG.js → abnfDiagram-VRR7QNED-Dt7QEF5H.js} +1 -1
- package/web/assets/app-C1j063QX.js +2 -0
- package/web/assets/app-options-98-a6ivv.js +25 -0
- package/web/assets/{app-view-header-BSOI-wB6.js → app-view-header-BJ9CWTE1.js} +1 -1
- package/web/assets/{arc-zngHsMKV.js → arc-PFB0H7oD.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-BLklLqRP.js → architectureDiagram-ZJ3FMSHR-BHLqEXlZ.js} +1 -1
- package/web/assets/{ask-tray-B4SPj-fH.js → ask-tray-C3gBesHJ.js} +1 -1
- package/web/assets/{blockDiagram-677ZJIJ3-yhC2fEtu.js → blockDiagram-677ZJIJ3-UbnYoYDg.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-DMcweQme.js → c4Diagram-LMCZKHZV-4OSx1V4W.js} +1 -1
- package/web/assets/channel-BuvY5cQN.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-DD-S6n8h.js → chunk-2Q5K7J3B-CgxJ1aJb.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-DFIAKRv_.js → chunk-32BRIVSS-Bfwxg2LM.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-B23APb3c.js → chunk-5VM5RSS4-BZN9a2hI.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-BSVEB7db.js → chunk-EX3LRPZG-CrH6hhqq.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-B6qGd0ng.js → chunk-JWPE2WC7-Ddn7ZSp8.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-N4T1-ew1.js → chunk-MOJQB5TN-9lJ7f_hI.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-6oc6i_Xw.js → chunk-RYQCIY6F-QrFo_00s.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-Dftgm9ck.js → chunk-V7JOEXUC-Dn0lQjVf.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-DfjqwEsO.js → chunk-VR4S4FIN-DXVPySkB.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-CsFsQYow.js → chunk-XXDRQBXY-CBiFDOdZ.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-DCYNCJhe.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-DCYNCJhe.js +1 -0
- package/web/assets/client-DBJY3XDL.js +43 -0
- package/web/assets/client-DKJs0w4y.js +6 -0
- package/web/assets/{command-D_9V8ejS.js → command-onjTnuXT.js} +1 -1
- package/web/assets/{copy-context-CevDWX59.js → copy-context-CciRaLNN.js} +1 -1
- package/web/assets/{cose-bilkent-JH36ORCC-DzKsIuDb.js → cose-bilkent-JH36ORCC-CGRic2um.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-AL45ZHbw.js → cynefin-VYW2F7L2-Blk0Lu0l.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-DSbBegxZ.js → cynefinDiagram-TSTJHNR4-DpnnHRDK.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-BaG6MGUE.js → dagre-VKFMJZFB-llzMokKp.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-WbDotTX5.js → diagram-FQU43EPY-DC6xHovR.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-DMbouYVI.js → diagram-G47NLZAW-B8GVS3EA.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-DoEuaWd_.js → diagram-NH7WQ7WH-DWUaARfh.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-BjGd-PoH.js → diagram-OA4YK3LP-9Uaes_Yi.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-CXHi27jF.js → diagram-WEI45ONY-DPPTBWud.js} +1 -1
- package/web/assets/{document-CVJCfWiQ.js → document-D23WzFsW.js} +3 -3
- package/web/assets/{ebnfDiagram-CCIWWBDH-B5vMUDOs.js → ebnfDiagram-CCIWWBDH-BPrKneNw.js} +1 -1
- package/web/assets/{entities-BTKzWJ-r.js → entities-DPg0B8vd.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-DhO7ayzF.js → erDiagram-Q63AITRT-CFqYBcnb.js} +1 -1
- package/web/assets/{file-viewer-D1HK9J3A.js → file-viewer-Ck73SsLA.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-CublTUlC.js → flowDiagram-23GEKE2U-C7RpYqaT.js} +1 -1
- package/web/assets/folder-filter-bar-DzejUj87.js +4 -0
- package/web/assets/{folder-image-L_AjkRfI.js → folder-image-BjIicmQ6.js} +1 -1
- package/web/assets/folder-landing-CCIXZfkp.js +55 -0
- package/web/assets/folder-list-TuRwuTss.js +1 -0
- package/web/assets/folder-mobile-bar-Bqnfwi-z.js +1 -0
- package/web/assets/folder-plugin-view-CrinCX1o.js +2 -0
- package/web/assets/{ganttDiagram-NO4QXBWP-BLMJWerM.js → ganttDiagram-NO4QXBWP-Cz2sxU8F.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-Br0E6qyZ.js → gitGraphDiagram-IHSO6WYX-BSuz0yF-.js} +1 -1
- package/web/assets/index-DLn_2LPp.js +2 -0
- package/web/assets/index-uY9jLiL5.css +1 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-Dgz0npJO.js → infoDiagram-FWYZ7A6U-CpeKgqoS.js} +1 -1
- package/web/assets/input-Bz_puamS.js +9 -0
- package/web/assets/{ishikawaDiagram-FXEZZL3T-rY5pdS3b.js → ishikawaDiagram-FXEZZL3T-5c2Tmknb.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-CKZALyR6.js → journeyDiagram-5HDEW3XC-Cu-wRkR4.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-EGPY9Vda.js → kanban-definition-HUTT4EX6-BunSqkPe.js} +1 -1
- package/web/assets/{linear-DkJW9OwI.js → linear-C8e8bGVv.js} +1 -1
- package/web/assets/{mermaid.core-DVEpXYiF.js → mermaid.core-DLgjY1g2.js} +4 -4
- package/web/assets/{metadata-Bu_ffLUd.js → metadata-DEfJKlnd.js} +1 -1
- package/web/assets/{mindmap-definition-LN4V7U3C-BEOUVnDN.js → mindmap-definition-LN4V7U3C-W9ENYFOD.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-DThjl3eK.js → pegDiagram-2B236MQR-0vPlHCFp.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-DzmoGgw2.js → pieDiagram-ENE6RG2P-CakaveRV.js} +1 -1
- package/web/assets/{project-search-dialog-e2Jmhvfq.js → project-search-dialog-BSKA4peX.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-BqT8U3FS.js → quadrantDiagram-ABIIQ3AL-jxHw_egj.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-1k1tfPk1.js → railroadDiagram-RFXS5EU6-lIjDXPZt.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-bM4RTd2J.js → requirementDiagram-TGXJPOKE-C-JiT4bc.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-TCgfqDLy.js → sankeyDiagram-HTMAVEWB-SuLPAw9X.js} +1 -1
- package/web/assets/{search-DQiFH2up.js → search-CKujW14f.js} +1 -1
- package/web/assets/{selection-popup-bqfgVd-L.js → selection-popup-CtxT9SWn.js} +3 -3
- package/web/assets/{sequenceDiagram-DBY2YBRQ-BRhGHI0i.js → sequenceDiagram-DBY2YBRQ-o9UwTkZR.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-EeUWgn0N.js → sizeCapture-X5ZJPWSS-ClbWGybF.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-BSOG758d.js → stateDiagram-2N3HPSRC-Ce4avoWx.js} +1 -1
- package/web/assets/stateDiagram-v2-6OUMAXLB-CsrPTZoJ.js +1 -0
- package/web/assets/{swimlanes-5IMT3BWC-DpCo4aEm.js → swimlanes-5IMT3BWC-DMZI4XZT.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-pShSVcTd.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-BpaObe6u.js → timeline-definition-FHXFAJF6-CPIpIgPN.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-ClvUzW8B.js → vennDiagram-L72KCM5P-_rha8uDZ.js} +1 -1
- package/web/assets/view-context-BDtIUHQy.js +229 -0
- package/web/assets/{viewer-capabilities-CFBwvpcI.js → viewer-capabilities-YYZqMs56.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-Dg1CX0Fl.js → wardleyDiagram-EHGQE667-D4EAOS35.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-C0c9zQTr.js → xychartDiagram-FW5EYKEG-DR8PwXpe.js} +1 -1
- package/web/index.html +2 -2
- package/kernel/packages/agents/eslint.config.js +0 -10
- package/kernel/packages/agents/package.json +0 -75
- package/kernel/packages/agents/src/agent-card.tsx +0 -334
- package/kernel/packages/agents/src/client.tsx +0 -13
- package/kernel/packages/agents/src/index.ts +0 -18
- package/kernel/packages/agents/src/manifest.ts +0 -7
- package/kernel/packages/agents/src/node.ts +0 -447
- package/kernel/packages/agents/src/profiles.ts +0 -137
- package/kernel/packages/agents/src/resolve.ts +0 -69
- package/kernel/packages/agents/src/server.ts +0 -60
- package/kernel/packages/agents/src/types.ts +0 -82
- package/kernel/packages/agents/src/viewer.tsx +0 -370
- package/kernel/packages/agents/test/profiles.test.mjs +0 -88
- package/kernel/packages/agents/test/resolve.test.mjs +0 -729
- package/kernel/packages/agents/tsconfig.json +0 -17
- package/kernel/packages/api/test/agents-loading.test.ts +0 -94
- package/kernel/packages/ui/src/components/folder-skill-list.tsx +0 -62
- package/web/assets/app-Debw-lUT.js +0 -2
- package/web/assets/app-options-UU_JlJQv.js +0 -25
- package/web/assets/channel-BgYLn5Cm.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-Fzhmfbkt.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-Fzhmfbkt.js +0 -1
- package/web/assets/client-C1WdY7jQ.js +0 -22
- package/web/assets/client-D__L4Hl0.js +0 -17
- package/web/assets/client-DoMhF9Tv.js +0 -5
- package/web/assets/folder-filter-bar-BHz7s9Vb.js +0 -4
- package/web/assets/folder-landing-Byh8W67L.js +0 -54
- package/web/assets/folder-list-jMyo-7pV.js +0 -1
- package/web/assets/folder-metadata-bCocvxpP.js +0 -2
- package/web/assets/folder-mobile-bar-DvvyOS-a.js +0 -1
- package/web/assets/folder-plugin-view-sib0Dwfw.js +0 -3
- package/web/assets/index-4Xh9UKim.js +0 -1
- package/web/assets/index-Bu75S2qS.js +0 -1
- package/web/assets/index-CPcMcCUY.js +0 -2
- package/web/assets/index-CXxkvzNJ.css +0 -1
- package/web/assets/select-CkOwL81C.js +0 -30
- package/web/assets/stateDiagram-v2-6OUMAXLB-DUeGDz1r.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-BGOaCfyV.js +0 -8
- package/web/assets/use-folder-filter-hA6fa7nm.js +0 -1
- package/web/assets/view-context-CVvDw2JM.js +0 -229
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
export type AgentClientId = "generic" | "codex" | "claude" | "pi"
|
|
2
|
-
|
|
3
|
-
export type ClientFilter = AgentClientId | "all"
|
|
4
|
-
|
|
5
|
-
export type SourceLevel =
|
|
6
|
-
"project" | "parent" | "repository" | "linked" | "global"
|
|
7
|
-
|
|
8
|
-
export type AgentSourceStatus = "loaded" | "unreadable" | "unsafe"
|
|
9
|
-
|
|
10
|
-
export type AgentSource = {
|
|
11
|
-
/** Physical file identity (resolved realpath), used for deduplication. */
|
|
12
|
-
id: string
|
|
13
|
-
/** Canonical physical path shown next to the source text. */
|
|
14
|
-
path: string
|
|
15
|
-
/** Literal file paths that resolved to this physical file (e.g. symlinks). */
|
|
16
|
-
sourcePaths: string[]
|
|
17
|
-
/** Leaf file name, e.g. AGENTS.md or CLAUDE.local.md. */
|
|
18
|
-
filename: string
|
|
19
|
-
/** Physical containing directory. */
|
|
20
|
-
directory: string
|
|
21
|
-
/** Where the source sits in the project → parents → global order. */
|
|
22
|
-
level: SourceLevel
|
|
23
|
-
/** Read result. Unreadable sources stay visible instead of hidden. */
|
|
24
|
-
status: AgentSourceStatus
|
|
25
|
-
/** Full instruction text (empty when the status is not `loaded`). */
|
|
26
|
-
text: string
|
|
27
|
-
/** Byte length of the physical file. */
|
|
28
|
-
size: number
|
|
29
|
-
/** Clients whose discovery includes this physical file. */
|
|
30
|
-
clientIds: AgentClientId[]
|
|
31
|
-
/** True when this file is a per-directory override (AGENTS.override.md). */
|
|
32
|
-
override: boolean
|
|
33
|
-
/** All listed sources apply to the selected working folder. */
|
|
34
|
-
applies: true
|
|
35
|
-
/** Human reason for an unreadable source. */
|
|
36
|
-
reason?: string
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export type AgentWarning =
|
|
40
|
-
| {
|
|
41
|
-
kind: "missing"
|
|
42
|
-
/** Directory or path that was expected but contained no instruction file. */
|
|
43
|
-
path: string
|
|
44
|
-
/** Clients that expected a file here. */
|
|
45
|
-
clientIds: AgentClientId[]
|
|
46
|
-
reason: string
|
|
47
|
-
}
|
|
48
|
-
| {
|
|
49
|
-
kind: "unsupported"
|
|
50
|
-
/** Clients whose configuration is not applied by this preview. */
|
|
51
|
-
clientIds: AgentClientId[]
|
|
52
|
-
reason: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* One directory in the ancestor walk: the working folder first, then every
|
|
57
|
-
* parent up to the selected client's boundary. Directories without any
|
|
58
|
-
* instruction file stay visible so the full chain going up is always shown.
|
|
59
|
-
*/
|
|
60
|
-
export type AgentChainEntry = {
|
|
61
|
-
/** Physical directory, ordered from the working folder up to the boundary. */
|
|
62
|
-
directory: string
|
|
63
|
-
/** Where the directory sits in the working-folder → boundary order. */
|
|
64
|
-
level: SourceLevel
|
|
65
|
-
/** Instruction files found in this directory for the selected client. */
|
|
66
|
-
filenames: string[]
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export type AgentsResolution = {
|
|
70
|
-
/** Canonical working folder. */
|
|
71
|
-
folder: string
|
|
72
|
-
/** Nearest ancestor containing `.git`, or null when none is found. */
|
|
73
|
-
repositoryRoot: string | null
|
|
74
|
-
/** Client filter used for this resolution. */
|
|
75
|
-
client: ClientFilter
|
|
76
|
-
/** Ordered, deduplicated sources: project, parents, then global. */
|
|
77
|
-
sources: AgentSource[]
|
|
78
|
-
/** Non-fatal warnings (missing files, unapplied configuration). */
|
|
79
|
-
warnings: AgentWarning[]
|
|
80
|
-
/** The full ancestor walk: working folder up to the client's boundary. */
|
|
81
|
-
chain: AgentChainEntry[]
|
|
82
|
-
}
|
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
import { useMemo, useRef, useState } from "react"
|
|
2
|
-
import { useQuery } from "@tanstack/react-query"
|
|
3
|
-
import { ChevronDown, Info, TriangleAlert } from "lucide-react"
|
|
4
|
-
|
|
5
|
-
import type { FolderPluginViewProps } from "kennel-plugins/client"
|
|
6
|
-
import {
|
|
7
|
-
Collapsible,
|
|
8
|
-
CollapsibleContent,
|
|
9
|
-
CollapsibleTrigger,
|
|
10
|
-
} from "@raggle-ai/ui/components/collapsible"
|
|
11
|
-
import { FolderFilterBar } from "@raggle-ai/ui/components/folder-filter-bar"
|
|
12
|
-
import {
|
|
13
|
-
Select,
|
|
14
|
-
SelectContent,
|
|
15
|
-
SelectItem,
|
|
16
|
-
SelectTrigger,
|
|
17
|
-
SelectValue,
|
|
18
|
-
} from "@raggle-ai/ui/components/select"
|
|
19
|
-
import { useFolderFilter } from "@raggle-ai/ui/hooks/use-folder-filter"
|
|
20
|
-
import { cn } from "@raggle-ai/ui/lib/utils"
|
|
21
|
-
|
|
22
|
-
import type { AgentsResolution, AgentWarning } from "./types.ts"
|
|
23
|
-
import {
|
|
24
|
-
AgentCard,
|
|
25
|
-
agentCardClass,
|
|
26
|
-
CurrentFolderBox,
|
|
27
|
-
FolderRow,
|
|
28
|
-
} from "./agent-card.tsx"
|
|
29
|
-
import {
|
|
30
|
-
clientLabel,
|
|
31
|
-
FILTERS,
|
|
32
|
-
parseClientFilter,
|
|
33
|
-
PROFILES,
|
|
34
|
-
} from "./profiles.ts"
|
|
35
|
-
|
|
36
|
-
export type AgentsPluginData = Pick<
|
|
37
|
-
AgentsResolution,
|
|
38
|
-
"chain" | "client" | "sources"
|
|
39
|
-
> & {
|
|
40
|
-
folder: string
|
|
41
|
-
repositoryRoot: string | null
|
|
42
|
-
status: "available"
|
|
43
|
-
warnings: AgentWarning[]
|
|
44
|
-
linkedStatus?: "pending" | "ready" | "error"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function ClientSelect({
|
|
48
|
-
client,
|
|
49
|
-
href,
|
|
50
|
-
}: {
|
|
51
|
-
client: string
|
|
52
|
-
href: (subpath: string) => string
|
|
53
|
-
}) {
|
|
54
|
-
return (
|
|
55
|
-
<div className="flex items-center gap-2">
|
|
56
|
-
<label
|
|
57
|
-
className="text-[0.78rem] font-medium tracking-wide text-[#6c7a6d]
|
|
58
|
-
uppercase"
|
|
59
|
-
>
|
|
60
|
-
AI client
|
|
61
|
-
</label>
|
|
62
|
-
<Select
|
|
63
|
-
onValueChange={(value) => {
|
|
64
|
-
window.location.href = href(value)
|
|
65
|
-
}}
|
|
66
|
-
value={client}
|
|
67
|
-
>
|
|
68
|
-
<SelectTrigger aria-label="AI client" size="sm" className="w-44">
|
|
69
|
-
<SelectValue placeholder="Select client" />
|
|
70
|
-
</SelectTrigger>
|
|
71
|
-
<SelectContent>
|
|
72
|
-
{FILTERS.map((option) => (
|
|
73
|
-
<SelectItem key={option.id} value={option.id}>
|
|
74
|
-
{option.label}
|
|
75
|
-
</SelectItem>
|
|
76
|
-
))}
|
|
77
|
-
</SelectContent>
|
|
78
|
-
</Select>
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function AgentsPluginView({
|
|
84
|
-
folder,
|
|
85
|
-
folderId,
|
|
86
|
-
hostActions,
|
|
87
|
-
href,
|
|
88
|
-
request,
|
|
89
|
-
subpath,
|
|
90
|
-
}: FolderPluginViewProps) {
|
|
91
|
-
const client = parseClientFilter(subpath || null)
|
|
92
|
-
const retryLinked = useRef(false)
|
|
93
|
-
const query = useQuery({
|
|
94
|
-
enabled: Boolean(folder),
|
|
95
|
-
gcTime: 5 * 60_000,
|
|
96
|
-
queryFn: ({ signal }) => {
|
|
97
|
-
const headers = retryLinked.current
|
|
98
|
-
? { "x-kennel-retry-linked": "1" }
|
|
99
|
-
: undefined
|
|
100
|
-
retryLinked.current = false
|
|
101
|
-
return request(subpath, { signal, headers }) as Promise<AgentsPluginData>
|
|
102
|
-
},
|
|
103
|
-
queryKey: ["folder", folderId, "agents", subpath ?? ""],
|
|
104
|
-
staleTime: 15_000,
|
|
105
|
-
refetchInterval: (query) =>
|
|
106
|
-
query.state.data?.linkedStatus === "pending" ? 1_000 : false,
|
|
107
|
-
})
|
|
108
|
-
const data = query.data as AgentsPluginData | undefined
|
|
109
|
-
const sources = useMemo(() => data?.sources ?? [], [data])
|
|
110
|
-
const warnings = useMemo(() => data?.warnings ?? [], [data])
|
|
111
|
-
// Shared folder filter over each source's containing directory. Source
|
|
112
|
-
// directories can be the project folder, ancestors, or the global
|
|
113
|
-
// directory — `allowOutside` surfaces all of them as chips.
|
|
114
|
-
const filter = useFolderFilter(sources, {
|
|
115
|
-
allowOutside: true,
|
|
116
|
-
getFolder: (source) => source.directory,
|
|
117
|
-
rootFolder: folder,
|
|
118
|
-
})
|
|
119
|
-
const profile = PROFILES[client]
|
|
120
|
-
const [detailsOpen, setDetailsOpen] = useState(false)
|
|
121
|
-
const [warningsOpen, setWarningsOpen] = useState(false)
|
|
122
|
-
const currentFolder = data?.folder ?? folder
|
|
123
|
-
const hasOwnInstructions = sources.some(
|
|
124
|
-
(source) => source.level === "project",
|
|
125
|
-
)
|
|
126
|
-
// The full ancestor walk (working folder first, then parents up to the
|
|
127
|
-
// client's boundary). The current-folder box covers the first entry, so
|
|
128
|
-
// the chain list below shows every directory going up from it.
|
|
129
|
-
const parentChain = useMemo(() => (data?.chain ?? []).slice(1), [data])
|
|
130
|
-
const projectSources = useMemo(
|
|
131
|
-
() => filter.visibleItems.filter((source) => source.level === "project"),
|
|
132
|
-
[filter.visibleItems],
|
|
133
|
-
)
|
|
134
|
-
const globalSources = useMemo(
|
|
135
|
-
() => filter.visibleItems.filter((source) => source.level === "global"),
|
|
136
|
-
[filter.visibleItems],
|
|
137
|
-
)
|
|
138
|
-
const sourcesIn = (directory: string) =>
|
|
139
|
-
filter.visibleItems.filter((source) => source.directory === directory)
|
|
140
|
-
const sourceKey = (source: (typeof sources)[number]) =>
|
|
141
|
-
`${source.id}:${source.clientIds.join(",")}`
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<div
|
|
145
|
-
className="mx-auto grid w-full max-w-5xl min-w-0 grid-cols-1 gap-4 px-4
|
|
146
|
-
py-5"
|
|
147
|
-
>
|
|
148
|
-
<Collapsible onOpenChange={setDetailsOpen} open={detailsOpen}>
|
|
149
|
-
<header className="flex flex-wrap items-center justify-between gap-3">
|
|
150
|
-
<h1 className="m-0 text-2xl font-semibold text-[#233025]">Agents</h1>
|
|
151
|
-
<CollapsibleTrigger asChild>
|
|
152
|
-
<button
|
|
153
|
-
aria-expanded={detailsOpen}
|
|
154
|
-
aria-label={
|
|
155
|
-
detailsOpen ? "Hide client details" : "Show client details"
|
|
156
|
-
}
|
|
157
|
-
className="inline-flex items-center gap-1.5 rounded-md border
|
|
158
|
-
border-[#e7ebe7] bg-white px-2.5 py-1 text-[0.78rem] font-medium
|
|
159
|
-
text-[#4a554b] hover:bg-[#f3f5f2] focus-visible:outline-2
|
|
160
|
-
focus-visible:outline-[#56725e]"
|
|
161
|
-
type="button"
|
|
162
|
-
>
|
|
163
|
-
<Info className="size-4 text-[#6c7a6d]" />
|
|
164
|
-
Details
|
|
165
|
-
<ChevronDown
|
|
166
|
-
className={cn(
|
|
167
|
-
"size-3.5 text-[#6c7a6d] transition-transform",
|
|
168
|
-
detailsOpen && "rotate-180",
|
|
169
|
-
)}
|
|
170
|
-
/>
|
|
171
|
-
</button>
|
|
172
|
-
</CollapsibleTrigger>
|
|
173
|
-
</header>
|
|
174
|
-
<CollapsibleContent
|
|
175
|
-
className="data-[state=closed]:animate-out
|
|
176
|
-
data-[state=open]:animate-in"
|
|
177
|
-
>
|
|
178
|
-
<p
|
|
179
|
-
className="m-0 mt-3 flex items-start gap-2 text-[0.72rem]
|
|
180
|
-
text-[#6c7a6d]"
|
|
181
|
-
>
|
|
182
|
-
<TriangleAlert className="mt-0.5 size-3.5 shrink-0" />
|
|
183
|
-
<span>
|
|
184
|
-
{profile.note} {profile.limits}
|
|
185
|
-
</span>
|
|
186
|
-
</p>
|
|
187
|
-
</CollapsibleContent>
|
|
188
|
-
</Collapsible>
|
|
189
|
-
|
|
190
|
-
<div className="flex flex-wrap items-center gap-x-4 gap-y-2">
|
|
191
|
-
<ClientSelect client={client} href={href} />
|
|
192
|
-
{filter.choices.length > 1 ? (
|
|
193
|
-
<FolderFilterBar
|
|
194
|
-
ariaLabel="Filter by instruction folder"
|
|
195
|
-
choices={filter.choices}
|
|
196
|
-
onSelect={filter.setSelected}
|
|
197
|
-
selected={filter.effectiveSelected}
|
|
198
|
-
total={filter.total}
|
|
199
|
-
/>
|
|
200
|
-
) : null}
|
|
201
|
-
</div>
|
|
202
|
-
|
|
203
|
-
{data ? (
|
|
204
|
-
<section
|
|
205
|
-
className={agentCardClass}
|
|
206
|
-
aria-label="Current folder instructions"
|
|
207
|
-
>
|
|
208
|
-
{!projectSources.length ? (
|
|
209
|
-
<CurrentFolderBox
|
|
210
|
-
createAction={hostActions?.createAgentFile}
|
|
211
|
-
folder={currentFolder}
|
|
212
|
-
hasOwn={hasOwnInstructions}
|
|
213
|
-
onCreated={() => void query.refetch()}
|
|
214
|
-
/>
|
|
215
|
-
) : null}
|
|
216
|
-
|
|
217
|
-
{projectSources.length ? (
|
|
218
|
-
<div className="grid grid-cols-1">
|
|
219
|
-
{projectSources.map((source) => (
|
|
220
|
-
<AgentCard
|
|
221
|
-
hostActions={hostActions}
|
|
222
|
-
key={sourceKey(source)}
|
|
223
|
-
source={source}
|
|
224
|
-
embedded
|
|
225
|
-
/>
|
|
226
|
-
))}
|
|
227
|
-
</div>
|
|
228
|
-
) : null}
|
|
229
|
-
</section>
|
|
230
|
-
) : null}
|
|
231
|
-
|
|
232
|
-
{data?.linkedStatus === "pending" ? (
|
|
233
|
-
<p role="status" className="m-0 text-sm text-[#6c7a6d]">
|
|
234
|
-
Local instructions loaded. Looking for linked project instructions…
|
|
235
|
-
</p>
|
|
236
|
-
) : null}
|
|
237
|
-
{data?.linkedStatus === "error" ? (
|
|
238
|
-
<p role="status" className="m-0 text-sm text-[#9c3a3a]">
|
|
239
|
-
Linked project discovery is unavailable. Local instructions are shown.
|
|
240
|
-
<button
|
|
241
|
-
onClick={() => {
|
|
242
|
-
retryLinked.current = true
|
|
243
|
-
void query.refetch()
|
|
244
|
-
}}
|
|
245
|
-
>
|
|
246
|
-
Retry linked instructions
|
|
247
|
-
</button>
|
|
248
|
-
</p>
|
|
249
|
-
) : null}
|
|
250
|
-
{query.isPending ? (
|
|
251
|
-
<p className="m-0 text-sm text-[#6c7a6d]">Loading…</p>
|
|
252
|
-
) : null}
|
|
253
|
-
{query.error ? (
|
|
254
|
-
<p role="alert" className="m-0 text-sm text-[#9c3a3a]">
|
|
255
|
-
{String(query.error)}{" "}
|
|
256
|
-
<button onClick={() => void query.refetch()}>
|
|
257
|
-
Retry instructions
|
|
258
|
-
</button>
|
|
259
|
-
</p>
|
|
260
|
-
) : null}
|
|
261
|
-
|
|
262
|
-
{warnings.length ? (
|
|
263
|
-
<Collapsible onOpenChange={setWarningsOpen} open={warningsOpen}>
|
|
264
|
-
<CollapsibleTrigger asChild>
|
|
265
|
-
<button
|
|
266
|
-
aria-expanded={warningsOpen}
|
|
267
|
-
aria-label={warningsOpen ? "Hide warnings" : "Show warnings"}
|
|
268
|
-
className="inline-flex items-center gap-1.5 rounded-md border
|
|
269
|
-
border-[#e7ebe7] bg-white px-2.5 py-1 text-[0.78rem] font-medium
|
|
270
|
-
text-[#4a554b] hover:bg-[#f3f5f2] focus-visible:outline-2
|
|
271
|
-
focus-visible:outline-[#56725e]"
|
|
272
|
-
type="button"
|
|
273
|
-
>
|
|
274
|
-
<TriangleAlert className="size-4 text-[#6c7a6d]" />
|
|
275
|
-
Warnings
|
|
276
|
-
<span
|
|
277
|
-
className="rounded-full bg-[#f3f5f2] px-1.5 py-0.5
|
|
278
|
-
text-[0.68rem] text-[#6c7a6d]"
|
|
279
|
-
>
|
|
280
|
-
{warnings.length}
|
|
281
|
-
</span>
|
|
282
|
-
<ChevronDown
|
|
283
|
-
className={cn(
|
|
284
|
-
"size-3.5 text-[#6c7a6d] transition-transform",
|
|
285
|
-
warningsOpen && "rotate-180",
|
|
286
|
-
)}
|
|
287
|
-
/>
|
|
288
|
-
</button>
|
|
289
|
-
</CollapsibleTrigger>
|
|
290
|
-
<CollapsibleContent
|
|
291
|
-
className="data-[state=closed]:animate-out
|
|
292
|
-
data-[state=open]:animate-in"
|
|
293
|
-
>
|
|
294
|
-
<ul className="m-0 mt-3 list-none space-y-1 p-0">
|
|
295
|
-
{warnings.map((warning) => (
|
|
296
|
-
<li
|
|
297
|
-
className="text-[0.72rem] text-[#6c7a6d]"
|
|
298
|
-
key={`${warning.kind}:${"path" in warning ? warning.path : ""}:${warning.clientIds.join(",")}`}
|
|
299
|
-
>
|
|
300
|
-
{"path" in warning ? `${warning.path} — ` : ""}
|
|
301
|
-
{warning.reason} (
|
|
302
|
-
{warning.clientIds.map((id) => clientLabel(id)).join(", ")})
|
|
303
|
-
</li>
|
|
304
|
-
))}
|
|
305
|
-
</ul>
|
|
306
|
-
</CollapsibleContent>
|
|
307
|
-
</Collapsible>
|
|
308
|
-
) : null}
|
|
309
|
-
|
|
310
|
-
{parentChain.length ? (
|
|
311
|
-
<div className="grid grid-cols-1 gap-4">
|
|
312
|
-
{parentChain.map((entry) => {
|
|
313
|
-
const entrySources = sourcesIn(entry.directory)
|
|
314
|
-
return (
|
|
315
|
-
<section
|
|
316
|
-
className={cn(agentCardClass, "scroll-mt-6")}
|
|
317
|
-
aria-label={`Instructions for ${entry.directory}`}
|
|
318
|
-
id={`agents-folder-${entry.directory}`}
|
|
319
|
-
key={entry.directory}
|
|
320
|
-
>
|
|
321
|
-
{!entrySources.length ? (
|
|
322
|
-
<FolderRow
|
|
323
|
-
createAction={hostActions?.createAgentFile}
|
|
324
|
-
entry={entry}
|
|
325
|
-
onCreated={() => void query.refetch()}
|
|
326
|
-
/>
|
|
327
|
-
) : null}
|
|
328
|
-
{entrySources.length ? (
|
|
329
|
-
<div className="grid grid-cols-1">
|
|
330
|
-
{entrySources.map((source) => (
|
|
331
|
-
<AgentCard
|
|
332
|
-
hostActions={hostActions}
|
|
333
|
-
key={sourceKey(source)}
|
|
334
|
-
source={source}
|
|
335
|
-
embedded
|
|
336
|
-
/>
|
|
337
|
-
))}
|
|
338
|
-
</div>
|
|
339
|
-
) : null}
|
|
340
|
-
</section>
|
|
341
|
-
)
|
|
342
|
-
})}
|
|
343
|
-
</div>
|
|
344
|
-
) : null}
|
|
345
|
-
|
|
346
|
-
{globalSources.length ? (
|
|
347
|
-
<section className="scroll-mt-6">
|
|
348
|
-
<div className="flex items-baseline gap-2">
|
|
349
|
-
<h3
|
|
350
|
-
className="m-0 text-[0.82rem] font-semibold"
|
|
351
|
-
style={{ color: "#315c3b" }}
|
|
352
|
-
>
|
|
353
|
-
Global instructions
|
|
354
|
-
</h3>
|
|
355
|
-
<span className="text-[0.7rem] text-[#6c7a6d]">Global</span>
|
|
356
|
-
</div>
|
|
357
|
-
<div className="mt-1 grid grid-cols-1 gap-3">
|
|
358
|
-
{globalSources.map((source) => (
|
|
359
|
-
<AgentCard
|
|
360
|
-
hostActions={hostActions}
|
|
361
|
-
key={sourceKey(source)}
|
|
362
|
-
source={source}
|
|
363
|
-
/>
|
|
364
|
-
))}
|
|
365
|
-
</div>
|
|
366
|
-
</section>
|
|
367
|
-
) : null}
|
|
368
|
-
</div>
|
|
369
|
-
)
|
|
370
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
CLIENT_ORDER,
|
|
5
|
-
clientLabel,
|
|
6
|
-
FILTERS,
|
|
7
|
-
parseClientFilter,
|
|
8
|
-
PROFILES,
|
|
9
|
-
} from "../src/profiles.ts"
|
|
10
|
-
|
|
11
|
-
describe("client profiles", () => {
|
|
12
|
-
test("the filter list is exactly the four clients", () => {
|
|
13
|
-
expect(FILTERS.map((option) => option.id)).toEqual([
|
|
14
|
-
"generic",
|
|
15
|
-
"codex",
|
|
16
|
-
"claude",
|
|
17
|
-
"pi",
|
|
18
|
-
])
|
|
19
|
-
expect(FILTERS.some((option) => option.id.includes("repository"))).toBe(
|
|
20
|
-
false,
|
|
21
|
-
)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
test("unknown filter values resolve to pi by default", () => {
|
|
25
|
-
expect(parseClientFilter(null)).toBe("pi")
|
|
26
|
-
expect(parseClientFilter(undefined)).toBe("pi")
|
|
27
|
-
expect(parseClientFilter("")).toBe("pi")
|
|
28
|
-
expect(parseClientFilter("bogus")).toBe("pi")
|
|
29
|
-
expect(parseClientFilter("all")).toBe("pi")
|
|
30
|
-
expect(parseClientFilter("generic")).toBe("generic")
|
|
31
|
-
expect(parseClientFilter("codex")).toBe("codex")
|
|
32
|
-
expect(parseClientFilter("claude")).toBe("claude")
|
|
33
|
-
expect(parseClientFilter("pi")).toBe("pi")
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test("generic is a root-walking reference view with no verified claim", () => {
|
|
37
|
-
expect(PROFILES.generic.scope).toBe("root")
|
|
38
|
-
expect(PROFILES.generic.files).toEqual(["AGENTS.md"])
|
|
39
|
-
expect(PROFILES.generic.verified).toBe(false)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
test("codex stops at git, prefers AGENTS.override.md, and honors CODEX_HOME", () => {
|
|
43
|
-
expect(PROFILES.codex.scope).toBe("git")
|
|
44
|
-
expect(PROFILES.codex.files).toEqual(["AGENTS.override.md", "AGENTS.md"])
|
|
45
|
-
expect(PROFILES.codex.filesMode).toBe("first")
|
|
46
|
-
expect(PROFILES.codex.globalEnv).toBe("CODEX_HOME")
|
|
47
|
-
expect(PROFILES.codex.globalFiles).toEqual([
|
|
48
|
-
"AGENTS.override.md",
|
|
49
|
-
"AGENTS.md",
|
|
50
|
-
])
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test("claude loads the three documented ancestor files and walks to root", () => {
|
|
54
|
-
expect(PROFILES.claude.scope).toBe("root")
|
|
55
|
-
expect(PROFILES.claude.files).toEqual([
|
|
56
|
-
"CLAUDE.md",
|
|
57
|
-
".claude/CLAUDE.md",
|
|
58
|
-
"CLAUDE.local.md",
|
|
59
|
-
])
|
|
60
|
-
expect(PROFILES.claude.filesMode).toBe("all")
|
|
61
|
-
expect(PROFILES.claude.globalDir).toBe(".claude")
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test("pi uses the documented first-match candidate list and honors PI_CODING_AGENT_DIR", () => {
|
|
65
|
-
expect(PROFILES.pi.scope).toBe("root")
|
|
66
|
-
expect(PROFILES.pi.files).toEqual([
|
|
67
|
-
"AGENTS.override.md",
|
|
68
|
-
"AGENTS.md",
|
|
69
|
-
"AGENTS.MD",
|
|
70
|
-
"CLAUDE.md",
|
|
71
|
-
"CLAUDE.MD",
|
|
72
|
-
])
|
|
73
|
-
expect(PROFILES.pi.filesMode).toBe("first")
|
|
74
|
-
expect(PROFILES.pi.globalEnv).toBe("PI_CODING_AGENT_DIR")
|
|
75
|
-
expect(PROFILES.pi.globalFiles).toEqual(PROFILES.pi.files)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test("client order keeps the stable inventory order", () => {
|
|
79
|
-
expect(CLIENT_ORDER).toEqual(["generic", "codex", "claude", "pi"])
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test("labels map back to the client ids", () => {
|
|
83
|
-
expect(clientLabel("generic")).toBe("Generic")
|
|
84
|
-
expect(clientLabel("codex")).toBe("Codex")
|
|
85
|
-
expect(clientLabel("claude")).toBe("Claude Code")
|
|
86
|
-
expect(clientLabel("pi")).toBe("Pi")
|
|
87
|
-
})
|
|
88
|
-
})
|