toga-ai 1.0.80 → 1.0.82
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/.claude/settings.json +21 -0
- package/knowledge/2.0/apps/toga2-hub/INDEX.md +3 -3
- package/knowledge/2.0/apps/toga2-hub/architecture.md +3 -3
- package/knowledge/2.0/apps/toga2-hub/features/acl-permissions-rendering.md +2 -2
- package/knowledge/INDEX.md +1 -1
- package/knowledge/registry.json +1 -1
- package/package.json +1 -1
- package/scripts/hooks/kickoff-gate.js +180 -0
- package/skills/kickoff/SKILL.md +10 -0
package/.claude/settings.json
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
|
+
"UserPromptSubmit": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "node \".claude/hooks/toga/kickoff-gate.js\"",
|
|
9
|
+
"timeout": 3000
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
3
14
|
"PreToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "Bash|Read|Grep|Glob|Edit|Write|MultiEdit|NotebookEdit|Task|WebFetch|WebSearch",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "node \".claude/hooks/toga/kickoff-gate.js\"",
|
|
21
|
+
"timeout": 3000
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
4
25
|
{
|
|
5
26
|
"matcher": "Bash",
|
|
6
27
|
"hooks": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# toga2-hub (
|
|
1
|
+
# toga2-hub (TOGa Hub) — 2.0 knowledge
|
|
2
2
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
|
-
| [
|
|
6
|
-
| [ACL Record Permissions — Rendering Semantics (Permissions Page)](features/acl-permissions-rendering.md) | How the
|
|
5
|
+
| [TOGa Hub Architecture](architecture.md) | toga2-hub (TOGa Hub) is a React + TypeScript single-page admin portal for the TOGA platform. | toga2-hub/src/main.tsx, toga2-hub/src/routes.tsx, toga2-hub/src/App.tsx, toga2-hub/src/api/axiosInstance.ts, toga2-hub/src/api/apiFunctions.ts, toga2-hub/src/utils/queryHelpers.ts, toga2-hub/src/contexts/AuthContext.tsx, toga2-hub/src/stores/useUserStore.ts |
|
|
6
|
+
| [ACL Record Permissions — Rendering Semantics (Permissions Page)](features/acl-permissions-rendering.md) | How the TOGa Hub Permissions page must interpret the api2 endpoint `GET /v2/acl-record-permissions/getAclRecordPermissionsByRecordAndRole` and turn its response | toga2-hub/src/pages/Permissions/api/permissionsApi.ts, toga2-hub/src/pages/Permissions/utils/permissionsUtils.ts, toga2-hub/src/pages/Permissions/viewModel/useRolesViewModel.ts, toga2-hub/src/hooks/useAclPermissionsByRoleQuery.ts, toga2-hub/src/pages/Permissions/view/RolesView/RoleDetailView/LogicGroupView.tsx, toga2-hub/src/pages/Permissions/view/RecordsView/RecordDetailView/RecordDetailView.tsx, toga2-hub/src/hooks/useQueryData.ts |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: TOGa Hub Architecture
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: toga2-hub
|
|
5
|
-
project:
|
|
5
|
+
project: TOGa Hub
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
@@ -23,7 +23,7 @@ related:
|
|
|
23
23
|
|
|
24
24
|
## Summary
|
|
25
25
|
|
|
26
|
-
toga2-hub (
|
|
26
|
+
toga2-hub (TOGa Hub) is a React + TypeScript single-page admin portal for the TOGA
|
|
27
27
|
platform. It is a pure frontend client — all business logic and persistence live in the
|
|
28
28
|
`api2` backend (framework 2.0), which Hub talks to exclusively over REST. The portal is
|
|
29
29
|
multi-tenant: it selects both its API endpoint and theme from the hostname it is served
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: ACL Record Permissions — Rendering Semantics (Permissions Page)
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: toga2-hub
|
|
5
|
-
project:
|
|
5
|
+
project: TOGa Hub
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
@@ -21,7 +21,7 @@ related: []
|
|
|
21
21
|
|
|
22
22
|
## Summary
|
|
23
23
|
|
|
24
|
-
How the
|
|
24
|
+
How the TOGa Hub Permissions page must interpret the api2 endpoint
|
|
25
25
|
`GET /v2/acl-record-permissions/getAclRecordPermissionsByRecordAndRole` and turn its
|
|
26
26
|
response into the green-check / red-X permissions grid. Semantics confirmed in a design
|
|
27
27
|
review with jcardinal (Naperville, 2026-06-09); an earlier implementation over-thought
|
package/knowledge/INDEX.md
CHANGED
|
@@ -18,7 +18,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
18
18
|
- **toga2-supply** (TOGa Supply) — 2 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
|
19
19
|
- **saml** (SAML SSO Gateway) — 2 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
|
|
20
20
|
- **toga2-view** (TOGa View Frontend) — 0 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
|
|
21
|
-
- **toga2-hub** (
|
|
21
|
+
- **toga2-hub** (TOGa Hub) — 2 doc(s) → [2.0/apps/toga2-hub/INDEX.md](2.0/apps/toga2-hub/INDEX.md)
|
|
22
22
|
|
|
23
23
|
## Clients
|
|
24
24
|
|
package/knowledge/registry.json
CHANGED
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
{ "repo": "toga2-view", "project": "TOGa View Frontend", "framework": "2.0", "role": "app", "dependsOn": ["api2"] },
|
|
11
11
|
{ "repo": "togadesk", "project": "TOGa Desk", "framework": "1.0", "role": "app", "dependsOn": [] },
|
|
12
12
|
{ "repo": "togaview", "project": "TOGa View", "framework": "1.0", "role": "app", "dependsOn": [] },
|
|
13
|
-
{ "repo": "toga2-hub", "project": "
|
|
13
|
+
{ "repo": "toga2-hub", "project": "TOGa Hub", "framework": "2.0", "role": "app", "dependsOn": ["api2"] }
|
|
14
14
|
|
|
15
15
|
]
|
package/package.json
CHANGED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* kickoff-gate.js — DETERMINISTIC enforcement of the /kickoff priming gate.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: the kickoff SKILL.md states a "hard gate" — when /kickoff is
|
|
8
|
+
* invoked you MUST complete priming (Steps 0–6) BEFORE touching the task with any
|
|
9
|
+
* Read/Grep/Glob/Edit/Write/Task call. Prose alone did not stop the model from
|
|
10
|
+
* jumping straight to investigation when /kickoff was followed by a long, specific
|
|
11
|
+
* task description. This hook makes the gate mechanical, not advisory.
|
|
12
|
+
*
|
|
13
|
+
* It registers for TWO events from a single file:
|
|
14
|
+
*
|
|
15
|
+
* UserPromptSubmit → if the submitted prompt invokes /kickoff, ARM the gate:
|
|
16
|
+
* write a session-scoped lock and inject a forceful reminder.
|
|
17
|
+
*
|
|
18
|
+
* PreToolUse → while the gate is armed for THIS session, ALLOW only the
|
|
19
|
+
* read-only priming steps (the priming Bash commands + the
|
|
20
|
+
* interview via AskUserQuestion/Skill, which don't match the
|
|
21
|
+
* PreToolUse matcher) and BLOCK every task tool
|
|
22
|
+
* (Read/Grep/Glob/Edit/Write/MultiEdit/NotebookEdit/Task/
|
|
23
|
+
* WebFetch/WebSearch and any non-priming Bash). The lock
|
|
24
|
+
* AUTO-CLEARS the moment `knowledge.js kickoff-preflight`
|
|
25
|
+
* runs — the definitive priming step that precedes Step 4's
|
|
26
|
+
* first Read. So the model literally cannot investigate or
|
|
27
|
+
* edit the task until it has actually primed.
|
|
28
|
+
*
|
|
29
|
+
* Fail-open: any internal/parse error allows the tool (a crashing gate must never
|
|
30
|
+
* brick every session). The SKILL.md prose remains the backup.
|
|
31
|
+
*
|
|
32
|
+
* Escape hatch: set KICKOFF_GATE_DISABLED=1 to disable enforcement entirely.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const fs = require('fs');
|
|
36
|
+
const path = require('path');
|
|
37
|
+
|
|
38
|
+
/* ---------- input: read stdin JSON (CC standard), fall back to env ---------- */
|
|
39
|
+
function readPayload() {
|
|
40
|
+
let raw = '';
|
|
41
|
+
try { raw = fs.readFileSync(0, 'utf8'); } catch (e) { /* no stdin */ }
|
|
42
|
+
let data = {};
|
|
43
|
+
if (raw && raw.trim()) {
|
|
44
|
+
try { data = JSON.parse(raw); } catch (e) { data = {}; }
|
|
45
|
+
}
|
|
46
|
+
// env fallbacks used by this harness's other hooks
|
|
47
|
+
if (!data.tool_name && process.env.CLAUDE_TOOL_NAME) data.tool_name = process.env.CLAUDE_TOOL_NAME;
|
|
48
|
+
if (!data.tool_input && process.env.CLAUDE_TOOL_INPUT) {
|
|
49
|
+
try { data.tool_input = JSON.parse(process.env.CLAUDE_TOOL_INPUT); } catch (e) {}
|
|
50
|
+
}
|
|
51
|
+
if (!data.prompt && process.env.CLAUDE_USER_PROMPT) data.prompt = process.env.CLAUDE_USER_PROMPT;
|
|
52
|
+
if (!data.session_id && process.env.CLAUDE_SESSION_ID) data.session_id = process.env.CLAUDE_SESSION_ID;
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function lockPath() {
|
|
57
|
+
return path.join(process.cwd(), '.claude', '.kickoff-gate.json');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readLock() {
|
|
61
|
+
try { return JSON.parse(fs.readFileSync(lockPath(), 'utf8')); } catch (e) { return null; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function writeLock(sessionId) {
|
|
65
|
+
try {
|
|
66
|
+
fs.mkdirSync(path.dirname(lockPath()), { recursive: true });
|
|
67
|
+
fs.writeFileSync(lockPath(), JSON.stringify({ session: sessionId || null, ts: Date.now() }) + '\n');
|
|
68
|
+
} catch (e) { /* non-fatal */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function clearLock() {
|
|
72
|
+
try { fs.unlinkSync(lockPath()); } catch (e) { /* already gone */ }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* A prompt invokes /kickoff if it carries any of these signals (covers the literal
|
|
76
|
+
* "/kickoff ...", the expanded skill command form, and the rendered skill header). */
|
|
77
|
+
function invokesKickoff(prompt) {
|
|
78
|
+
if (typeof prompt !== 'string' || !prompt) return false;
|
|
79
|
+
return (
|
|
80
|
+
/(^|\s)\/kickoff(\s|$)/.test(prompt) ||
|
|
81
|
+
/command-name>\s*\/?kickoff\s*<\/command-name>/i.test(prompt) ||
|
|
82
|
+
/Kickoff\s+—\s+prime a coding session/i.test(prompt)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Bash commands that ARE the read-only priming steps (Steps 0–3). Everything else
|
|
87
|
+
* is task work and stays blocked until preflight clears the gate. */
|
|
88
|
+
function isPrimingBash(command) {
|
|
89
|
+
if (typeof command !== 'string') return false;
|
|
90
|
+
return /knowledge\.js|kickoff-preflight|toga-ai\.version|npm\s+view\s+toga-ai|npx\s+toga-ai|registry\.json|rev-parse/i.test(command);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isPreflightBash(command) {
|
|
94
|
+
return typeof command === 'string' && /kickoff-preflight/i.test(command);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Tools that constitute touching the task — blocked while the gate is armed. */
|
|
98
|
+
const BLOCKED_TOOLS = new Set([
|
|
99
|
+
'Read', 'Grep', 'Glob', 'Edit', 'Write', 'MultiEdit',
|
|
100
|
+
'NotebookEdit', 'Task', 'Agent', 'WebFetch', 'WebSearch',
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
function blockMessage() {
|
|
104
|
+
return [
|
|
105
|
+
'🛑 KICKOFF GATE — priming is not complete.',
|
|
106
|
+
'',
|
|
107
|
+
'/kickoff was invoked this session. You MUST finish priming BEFORE touching the task',
|
|
108
|
+
'with any Read/Grep/Glob/Edit/Write/Task call — no matter how detailed the request after',
|
|
109
|
+
'/kickoff looks. That trailing text is the Step 2 task description, NOT permission to start.',
|
|
110
|
+
'',
|
|
111
|
+
'Do this now, in order (these are allowed while the gate is armed):',
|
|
112
|
+
' • Step 0 version check (cat .claude/toga-ai.version ; npm view toga-ai version)',
|
|
113
|
+
' • Step 1 resolve the team repo (probe for knowledge/registry.json)',
|
|
114
|
+
' • Step 2 interview the developer (framework / layer / repo(s) / client / task)',
|
|
115
|
+
' • Step 3 node "<TEAM_REPO>/knowledge.js" kickoff-preflight ... ← this AUTO-CLEARS the gate',
|
|
116
|
+
' • Step 4+ THEN read the knowledge docs and start the task',
|
|
117
|
+
'',
|
|
118
|
+
'The gate releases automatically the instant kickoff-preflight runs.',
|
|
119
|
+
'(Emergency override only: set KICKOFF_GATE_DISABLED=1.)',
|
|
120
|
+
].join('\n');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function main() {
|
|
124
|
+
if (process.env.KICKOFF_GATE_DISABLED === '1') process.exit(0);
|
|
125
|
+
|
|
126
|
+
const data = readPayload();
|
|
127
|
+
|
|
128
|
+
// ---- UserPromptSubmit: arm the gate when /kickoff is invoked ----
|
|
129
|
+
// Identify by the presence of a prompt and absence of a tool call.
|
|
130
|
+
const isPromptEvent =
|
|
131
|
+
data.hook_event_name === 'UserPromptSubmit' ||
|
|
132
|
+
(typeof data.prompt === 'string' && !data.tool_name);
|
|
133
|
+
|
|
134
|
+
if (isPromptEvent) {
|
|
135
|
+
if (invokesKickoff(data.prompt)) {
|
|
136
|
+
writeLock(data.session_id);
|
|
137
|
+
// stdout from UserPromptSubmit is injected into the model's context.
|
|
138
|
+
console.log(
|
|
139
|
+
'<system-reminder>KICKOFF GATE ARMED. Before ANY other tool call, complete priming ' +
|
|
140
|
+
'Steps 0–6 of the kickoff skill — version check, resolve team repo, interview, then ' +
|
|
141
|
+
'run `knowledge.js kickoff-preflight` (which releases the gate), THEN read docs and work. ' +
|
|
142
|
+
'The trailing task description is Step 2 input, not permission to start. ' +
|
|
143
|
+
'A hook will block Read/Grep/Edit/Write/Task until preflight runs.</system-reminder>'
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---- PreToolUse: enforce while armed for THIS session ----
|
|
150
|
+
const lock = readLock();
|
|
151
|
+
if (!lock) process.exit(0);
|
|
152
|
+
|
|
153
|
+
// Cross-session safety: only enforce for the session that armed the gate.
|
|
154
|
+
// If we can't match sessions, fall back to a 3-hour staleness window.
|
|
155
|
+
const sameSession = lock.session && data.session_id && lock.session === data.session_id;
|
|
156
|
+
const fresh = lock.ts && (Date.now() - lock.ts) < 3 * 60 * 60 * 1000;
|
|
157
|
+
if (!sameSession && !fresh) { clearLock(); process.exit(0); }
|
|
158
|
+
if (data.session_id && lock.session && !sameSession) process.exit(0);
|
|
159
|
+
|
|
160
|
+
const tool = data.tool_name || '';
|
|
161
|
+
const command = (data.tool_input && data.tool_input.command) || '';
|
|
162
|
+
|
|
163
|
+
if (tool === 'Bash') {
|
|
164
|
+
if (isPreflightBash(command)) { clearLock(); process.exit(0); } // priming reached — release
|
|
165
|
+
if (isPrimingBash(command)) process.exit(0); // other read-only priming step
|
|
166
|
+
console.log(blockMessage());
|
|
167
|
+
process.exit(2);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (BLOCKED_TOOLS.has(tool)) {
|
|
171
|
+
console.log(blockMessage());
|
|
172
|
+
process.exit(2);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Anything else (AskUserQuestion, Skill, TodoWrite, ExitPlanMode, …) is part of
|
|
176
|
+
// priming/interview — allow.
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try { main(); } catch (e) { process.exit(0); } // fail-open: never brick a session
|
package/skills/kickoff/SKILL.md
CHANGED
|
@@ -22,6 +22,16 @@ description: Start-of-session context loader for TOGA Technology projects. Run t
|
|
|
22
22
|
>
|
|
23
23
|
> Only after Step 5's "primed and ready" summary (and Step 6's plan, for non-trivial work)
|
|
24
24
|
> may you touch the task itself.
|
|
25
|
+
>
|
|
26
|
+
> **This gate is mechanically enforced — it is not just prose.** A hook
|
|
27
|
+
> (`hooks/toga/kickoff-gate.js`, wired on `UserPromptSubmit` + `PreToolUse`) arms the
|
|
28
|
+
> instant `/kickoff` is invoked and will **hard-block** `Read`, `Grep`, `Glob`, `Edit`,
|
|
29
|
+
> `Write`, `Task`, and any non-priming `Bash` until you actually prime. The only calls it
|
|
30
|
+
> permits while armed are the read-only priming steps (the version check, team-repo probe,
|
|
31
|
+
> `knowledge.js manifest`, the developer interview) and `knowledge.js kickoff-preflight` —
|
|
32
|
+
> **running preflight (Step 3) is what releases the gate.** So the correct response to a
|
|
33
|
+
> block message is never to fight it: run Steps 0–3, and the moment preflight executes you
|
|
34
|
+
> are free to read docs and work. (Emergency override only: `KICKOFF_GATE_DISABLED=1`.)
|
|
25
35
|
|
|
26
36
|
## Arguments — text passed after `/kickoff` never skips any step
|
|
27
37
|
|