sqlew 5.1.0 → 5.2.1
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 +2140 -2081
- package/LICENSE +190 -190
- package/NOTICE +24 -24
- package/README.md +204 -190
- package/dist/adapters/mysql-adapter.js +3 -3
- package/dist/adapters/postgresql-adapter.js +3 -3
- package/dist/cli/db-export.js +32 -32
- package/dist/cli/db-import.js +30 -30
- package/dist/cli/hooks/codex-transcript.d.ts +23 -0
- package/dist/cli/hooks/codex-transcript.d.ts.map +1 -0
- package/dist/cli/hooks/codex-transcript.js +134 -0
- package/dist/cli/hooks/codex-transcript.js.map +1 -0
- package/dist/cli/hooks/on-exit-plan.d.ts.map +1 -1
- package/dist/cli/hooks/on-exit-plan.js +72 -4
- package/dist/cli/hooks/on-exit-plan.js.map +1 -1
- package/dist/cli/hooks/on-prompt.d.ts.map +1 -1
- package/dist/cli/hooks/on-prompt.js +38 -16
- package/dist/cli/hooks/on-prompt.js.map +1 -1
- package/dist/cli/hooks/plan-processor.d.ts.map +1 -1
- package/dist/cli/hooks/plan-processor.js +16 -2
- package/dist/cli/hooks/plan-processor.js.map +1 -1
- package/dist/cli/hooks/pr-adr.js +5 -5
- package/dist/cli/hooks/stdin-parser.d.ts +43 -0
- package/dist/cli/hooks/stdin-parser.d.ts.map +1 -1
- package/dist/cli/hooks/stdin-parser.js +212 -6
- package/dist/cli/hooks/stdin-parser.js.map +1 -1
- package/dist/cli/hooks/track-plan.d.ts +13 -0
- package/dist/cli/hooks/track-plan.d.ts.map +1 -1
- package/dist/cli/hooks/track-plan.js +73 -18
- package/dist/cli/hooks/track-plan.js.map +1 -1
- package/dist/cli.js +48 -48
- package/dist/config/global-config.d.ts +7 -1
- package/dist/config/global-config.d.ts.map +1 -1
- package/dist/config/global-config.js +5 -26
- package/dist/config/global-config.js.map +1 -1
- package/dist/database/migrations/v4/20251126000000_v4_bootstrap.js +32 -32
- package/dist/database/migrations/v4/20251126000001_v4_migrate_data.d.ts.map +1 -1
- package/dist/database/migrations/v4/20251126000001_v4_migrate_data.js +2 -1
- package/dist/database/migrations/v4/20251126000001_v4_migrate_data.js.map +1 -1
- package/dist/database/migrations/v4/20260102204000_v4_fix_decision_set_example.js +3 -3
- package/dist/help-data/constraint.toml +259 -259
- package/dist/help-data/decision.toml +845 -845
- package/dist/help-data/queue.toml +134 -134
- package/dist/server/tool-schemas.js +30 -30
- package/dist/tests/docker/native/db-init.js +9 -9
- package/dist/tests/unit/hooks/codex-hook-normalization.test.d.ts +7 -0
- package/dist/tests/unit/hooks/codex-hook-normalization.test.d.ts.map +1 -0
- package/dist/tests/unit/hooks/codex-hook-normalization.test.js +112 -0
- package/dist/tests/unit/hooks/codex-hook-normalization.test.js.map +1 -0
- package/dist/tests/unit/hooks/grok-hook-normalization.test.d.ts +9 -0
- package/dist/tests/unit/hooks/grok-hook-normalization.test.d.ts.map +1 -0
- package/dist/tests/unit/hooks/grok-hook-normalization.test.js +136 -0
- package/dist/tests/unit/hooks/grok-hook-normalization.test.js.map +1 -0
- package/dist/tests/unit/hooks/grok-plan-template-injection.test.d.ts +7 -0
- package/dist/tests/unit/hooks/grok-plan-template-injection.test.d.ts.map +1 -0
- package/dist/tests/unit/hooks/grok-plan-template-injection.test.js +55 -0
- package/dist/tests/unit/hooks/grok-plan-template-injection.test.js.map +1 -0
- package/dist/tests/utils/db-schema.js +48 -48
- package/dist/tests/utils/test-helpers.js +48 -48
- package/dist/tools/constraints/actions/get.js +5 -5
- package/dist/utils/path-normalize.d.ts +23 -0
- package/dist/utils/path-normalize.d.ts.map +1 -0
- package/dist/utils/path-normalize.js +38 -0
- package/dist/utils/path-normalize.js.map +1 -0
- package/dist/utils/project-root.d.ts +7 -3
- package/dist/utils/project-root.d.ts.map +1 -1
- package/dist/utils/project-root.js +17 -3
- package/dist/utils/project-root.js.map +1 -1
- package/dist/watcher/base-watcher.d.ts +0 -4
- package/dist/watcher/base-watcher.d.ts.map +1 -1
- package/dist/watcher/base-watcher.js +11 -22
- package/dist/watcher/base-watcher.js.map +1 -1
- package/docs/ADR_CONCEPTS.md +152 -152
- package/docs/CLI_USAGE.md +392 -392
- package/docs/CONFIGURATION.md +157 -157
- package/docs/CROSS_DATABASE.md +66 -66
- package/docs/DATABASE_AUTH.md +135 -135
- package/docs/HOOKS_GUIDE.md +116 -67
- package/docs/MIGRATION_TO_SAAS.md +176 -176
- package/docs/SHARED_DATABASE.md +108 -108
- package/package.json +88 -88
- package/scripts/copy-help-data.js +19 -19
- package/scripts/filter-test-output.js +78 -78
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
# Queue Tool - Hook Queue Management
|
|
2
|
-
# Manage pending items in .sqlew/queue/pending.json
|
|
3
|
-
|
|
4
|
-
[tool]
|
|
5
|
-
name = "queue"
|
|
6
|
-
description = "Hook queue management - list, clear, remove pending items from .sqlew/queue/pending.json. Safe operations with lock mechanism to prevent race conditions with QueueWatcher."
|
|
7
|
-
|
|
8
|
-
# =============================================================================
|
|
9
|
-
# ACTIONS
|
|
10
|
-
# =============================================================================
|
|
11
|
-
|
|
12
|
-
[[actions]]
|
|
13
|
-
name = "list"
|
|
14
|
-
description = "List all pending items in the queue"
|
|
15
|
-
|
|
16
|
-
[[actions.params]]
|
|
17
|
-
name = "action"
|
|
18
|
-
type = "string"
|
|
19
|
-
required = true
|
|
20
|
-
description = "Must be \"list\""
|
|
21
|
-
|
|
22
|
-
[[actions.examples]]
|
|
23
|
-
title = "List queue items"
|
|
24
|
-
code = '''
|
|
25
|
-
{
|
|
26
|
-
"action": "list"
|
|
27
|
-
}
|
|
28
|
-
'''
|
|
29
|
-
explanation = "Show all pending decisions/constraints in the queue with their index, type, and metadata"
|
|
30
|
-
|
|
31
|
-
# -----------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
[[actions]]
|
|
34
|
-
name = "clear"
|
|
35
|
-
description = "Clear all items from the queue"
|
|
36
|
-
|
|
37
|
-
[[actions.params]]
|
|
38
|
-
name = "action"
|
|
39
|
-
type = "string"
|
|
40
|
-
required = true
|
|
41
|
-
description = "Must be \"clear\""
|
|
42
|
-
|
|
43
|
-
[[actions.examples]]
|
|
44
|
-
title = "Clear all queue items"
|
|
45
|
-
code = '''
|
|
46
|
-
{
|
|
47
|
-
"action": "clear"
|
|
48
|
-
}
|
|
49
|
-
'''
|
|
50
|
-
explanation = "Remove all pending items from the queue at once"
|
|
51
|
-
|
|
52
|
-
# -----------------------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
[[actions]]
|
|
55
|
-
name = "remove"
|
|
56
|
-
description = "Remove a specific item from the queue by index"
|
|
57
|
-
|
|
58
|
-
[[actions.params]]
|
|
59
|
-
name = "action"
|
|
60
|
-
type = "string"
|
|
61
|
-
required = true
|
|
62
|
-
description = "Must be \"remove\""
|
|
63
|
-
|
|
64
|
-
[[actions.params]]
|
|
65
|
-
name = "index"
|
|
66
|
-
type = "number"
|
|
67
|
-
required = true
|
|
68
|
-
description = "0-based index of the item to remove (use 'list' first to see indices)"
|
|
69
|
-
|
|
70
|
-
[[actions.examples]]
|
|
71
|
-
title = "Remove item at index 0"
|
|
72
|
-
code = '''
|
|
73
|
-
{
|
|
74
|
-
"action": "remove",
|
|
75
|
-
"index": 0
|
|
76
|
-
}
|
|
77
|
-
'''
|
|
78
|
-
explanation = "Remove the first item from the queue. Use 'list' first to see available indices."
|
|
79
|
-
|
|
80
|
-
# -----------------------------------------------------------------------------
|
|
81
|
-
|
|
82
|
-
[[actions]]
|
|
83
|
-
name = "help"
|
|
84
|
-
description = "Get queue tool documentation"
|
|
85
|
-
|
|
86
|
-
[[actions.params]]
|
|
87
|
-
name = "action"
|
|
88
|
-
type = "string"
|
|
89
|
-
required = true
|
|
90
|
-
description = "Must be \"help\""
|
|
91
|
-
|
|
92
|
-
[[actions.examples]]
|
|
93
|
-
title = "Get help"
|
|
94
|
-
code = '''
|
|
95
|
-
{
|
|
96
|
-
"action": "help"
|
|
97
|
-
}
|
|
98
|
-
'''
|
|
99
|
-
explanation = "Display queue tool documentation"
|
|
100
|
-
|
|
101
|
-
# -----------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
[[actions]]
|
|
104
|
-
name = "example"
|
|
105
|
-
description = "Get queue tool usage examples"
|
|
106
|
-
|
|
107
|
-
[[actions.params]]
|
|
108
|
-
name = "action"
|
|
109
|
-
type = "string"
|
|
110
|
-
required = true
|
|
111
|
-
description = "Must be \"example\""
|
|
112
|
-
|
|
113
|
-
[[actions.examples]]
|
|
114
|
-
title = "Get examples"
|
|
115
|
-
code = '''
|
|
116
|
-
{
|
|
117
|
-
"action": "example"
|
|
118
|
-
}
|
|
119
|
-
'''
|
|
120
|
-
explanation = "Display queue tool usage examples"
|
|
121
|
-
|
|
122
|
-
# =============================================================================
|
|
123
|
-
# USE CASES
|
|
124
|
-
# =============================================================================
|
|
125
|
-
|
|
126
|
-
[[use_cases]]
|
|
127
|
-
title = "Handle stuck items due to High Similarity"
|
|
128
|
-
description = "Items may remain in queue if they have 60%+ similarity to existing decisions"
|
|
129
|
-
steps = [
|
|
130
|
-
"1. Use `queue list` to see stuck items",
|
|
131
|
-
"2. Check existing decisions with `/sqlew search for <topic>`",
|
|
132
|
-
"3. If truly duplicate: Use `queue remove` with the item index",
|
|
133
|
-
"4. If different intent: Update the key to be more specific and retry"
|
|
134
|
-
]
|
|
1
|
+
# Queue Tool - Hook Queue Management
|
|
2
|
+
# Manage pending items in .sqlew/queue/pending.json
|
|
3
|
+
|
|
4
|
+
[tool]
|
|
5
|
+
name = "queue"
|
|
6
|
+
description = "Hook queue management - list, clear, remove pending items from .sqlew/queue/pending.json. Safe operations with lock mechanism to prevent race conditions with QueueWatcher."
|
|
7
|
+
|
|
8
|
+
# =============================================================================
|
|
9
|
+
# ACTIONS
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
[[actions]]
|
|
13
|
+
name = "list"
|
|
14
|
+
description = "List all pending items in the queue"
|
|
15
|
+
|
|
16
|
+
[[actions.params]]
|
|
17
|
+
name = "action"
|
|
18
|
+
type = "string"
|
|
19
|
+
required = true
|
|
20
|
+
description = "Must be \"list\""
|
|
21
|
+
|
|
22
|
+
[[actions.examples]]
|
|
23
|
+
title = "List queue items"
|
|
24
|
+
code = '''
|
|
25
|
+
{
|
|
26
|
+
"action": "list"
|
|
27
|
+
}
|
|
28
|
+
'''
|
|
29
|
+
explanation = "Show all pending decisions/constraints in the queue with their index, type, and metadata"
|
|
30
|
+
|
|
31
|
+
# -----------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
[[actions]]
|
|
34
|
+
name = "clear"
|
|
35
|
+
description = "Clear all items from the queue"
|
|
36
|
+
|
|
37
|
+
[[actions.params]]
|
|
38
|
+
name = "action"
|
|
39
|
+
type = "string"
|
|
40
|
+
required = true
|
|
41
|
+
description = "Must be \"clear\""
|
|
42
|
+
|
|
43
|
+
[[actions.examples]]
|
|
44
|
+
title = "Clear all queue items"
|
|
45
|
+
code = '''
|
|
46
|
+
{
|
|
47
|
+
"action": "clear"
|
|
48
|
+
}
|
|
49
|
+
'''
|
|
50
|
+
explanation = "Remove all pending items from the queue at once"
|
|
51
|
+
|
|
52
|
+
# -----------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
[[actions]]
|
|
55
|
+
name = "remove"
|
|
56
|
+
description = "Remove a specific item from the queue by index"
|
|
57
|
+
|
|
58
|
+
[[actions.params]]
|
|
59
|
+
name = "action"
|
|
60
|
+
type = "string"
|
|
61
|
+
required = true
|
|
62
|
+
description = "Must be \"remove\""
|
|
63
|
+
|
|
64
|
+
[[actions.params]]
|
|
65
|
+
name = "index"
|
|
66
|
+
type = "number"
|
|
67
|
+
required = true
|
|
68
|
+
description = "0-based index of the item to remove (use 'list' first to see indices)"
|
|
69
|
+
|
|
70
|
+
[[actions.examples]]
|
|
71
|
+
title = "Remove item at index 0"
|
|
72
|
+
code = '''
|
|
73
|
+
{
|
|
74
|
+
"action": "remove",
|
|
75
|
+
"index": 0
|
|
76
|
+
}
|
|
77
|
+
'''
|
|
78
|
+
explanation = "Remove the first item from the queue. Use 'list' first to see available indices."
|
|
79
|
+
|
|
80
|
+
# -----------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
[[actions]]
|
|
83
|
+
name = "help"
|
|
84
|
+
description = "Get queue tool documentation"
|
|
85
|
+
|
|
86
|
+
[[actions.params]]
|
|
87
|
+
name = "action"
|
|
88
|
+
type = "string"
|
|
89
|
+
required = true
|
|
90
|
+
description = "Must be \"help\""
|
|
91
|
+
|
|
92
|
+
[[actions.examples]]
|
|
93
|
+
title = "Get help"
|
|
94
|
+
code = '''
|
|
95
|
+
{
|
|
96
|
+
"action": "help"
|
|
97
|
+
}
|
|
98
|
+
'''
|
|
99
|
+
explanation = "Display queue tool documentation"
|
|
100
|
+
|
|
101
|
+
# -----------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
[[actions]]
|
|
104
|
+
name = "example"
|
|
105
|
+
description = "Get queue tool usage examples"
|
|
106
|
+
|
|
107
|
+
[[actions.params]]
|
|
108
|
+
name = "action"
|
|
109
|
+
type = "string"
|
|
110
|
+
required = true
|
|
111
|
+
description = "Must be \"example\""
|
|
112
|
+
|
|
113
|
+
[[actions.examples]]
|
|
114
|
+
title = "Get examples"
|
|
115
|
+
code = '''
|
|
116
|
+
{
|
|
117
|
+
"action": "example"
|
|
118
|
+
}
|
|
119
|
+
'''
|
|
120
|
+
explanation = "Display queue tool usage examples"
|
|
121
|
+
|
|
122
|
+
# =============================================================================
|
|
123
|
+
# USE CASES
|
|
124
|
+
# =============================================================================
|
|
125
|
+
|
|
126
|
+
[[use_cases]]
|
|
127
|
+
title = "Handle stuck items due to High Similarity"
|
|
128
|
+
description = "Items may remain in queue if they have 60%+ similarity to existing decisions"
|
|
129
|
+
steps = [
|
|
130
|
+
"1. Use `queue list` to see stuck items",
|
|
131
|
+
"2. Check existing decisions with `/sqlew search for <topic>`",
|
|
132
|
+
"3. If truly duplicate: Use `queue remove` with the item index",
|
|
133
|
+
"4. If different intent: Update the key to be more specific and retry"
|
|
134
|
+
]
|
|
@@ -121,38 +121,38 @@ export const TOOL_SCHEMAS = {
|
|
|
121
121
|
export const TOOL_DESCRIPTIONS = {
|
|
122
122
|
decision: 'Context Management - Store decisions with versioning and metadata. Use action: "help" for documentation.',
|
|
123
123
|
constraint: 'Architectural Rules - Define and manage project constraints with priorities. Use action: "help" for documentation.',
|
|
124
|
-
help: `**REQUIRED PARAMETER**: action (must be specified in ALL calls)
|
|
125
|
-
|
|
126
|
-
Help System - Query action documentation, parameters, and workflow guidance
|
|
127
|
-
|
|
128
|
-
Actions:
|
|
129
|
-
- query_action: Get action documentation with parameters and examples
|
|
130
|
-
- query_params: Get parameter list only (quick reference)
|
|
131
|
-
- query_tool: Get tool overview and all actions
|
|
132
|
-
- workflow_hints: Get common next actions after current action
|
|
133
|
-
- batch_guide: Get guidance for batch operations
|
|
134
|
-
- error_recovery: Analyze errors and suggest fixes
|
|
135
|
-
|
|
124
|
+
help: `**REQUIRED PARAMETER**: action (must be specified in ALL calls)
|
|
125
|
+
|
|
126
|
+
Help System - Query action documentation, parameters, and workflow guidance
|
|
127
|
+
|
|
128
|
+
Actions:
|
|
129
|
+
- query_action: Get action documentation with parameters and examples
|
|
130
|
+
- query_params: Get parameter list only (quick reference)
|
|
131
|
+
- query_tool: Get tool overview and all actions
|
|
132
|
+
- workflow_hints: Get common next actions after current action
|
|
133
|
+
- batch_guide: Get guidance for batch operations
|
|
134
|
+
- error_recovery: Analyze errors and suggest fixes
|
|
135
|
+
|
|
136
136
|
Use this tool to understand how to use other sqlew tools. Returns only requested information (80-95% token reduction vs legacy help).`,
|
|
137
|
-
example: `**REQUIRED PARAMETER**: action (must be specified in ALL calls)
|
|
138
|
-
|
|
139
|
-
Example System - Browse and search code examples for sqlew tools
|
|
140
|
-
|
|
141
|
-
Actions:
|
|
142
|
-
- get: Get examples by tool, action, or topic
|
|
143
|
-
- search: Search examples by keyword
|
|
144
|
-
- list_all: List all available examples with filtering
|
|
145
|
-
|
|
137
|
+
example: `**REQUIRED PARAMETER**: action (must be specified in ALL calls)
|
|
138
|
+
|
|
139
|
+
Example System - Browse and search code examples for sqlew tools
|
|
140
|
+
|
|
141
|
+
Actions:
|
|
142
|
+
- get: Get examples by tool, action, or topic
|
|
143
|
+
- search: Search examples by keyword
|
|
144
|
+
- list_all: List all available examples with filtering
|
|
145
|
+
|
|
146
146
|
Use this tool to find working code snippets. Returns only requested examples (token-efficient).`,
|
|
147
|
-
use_case: `**REQUIRED PARAMETER**: action (must be specified in ALL calls)
|
|
148
|
-
|
|
149
|
-
Use Case Catalog - Browse and search complete workflow scenarios
|
|
150
|
-
|
|
151
|
-
Actions:
|
|
152
|
-
- get: Get complete use case workflow by ID
|
|
153
|
-
- search: Search use cases by keyword/category
|
|
154
|
-
- list_all: List all use cases with filtering and pagination
|
|
155
|
-
|
|
147
|
+
use_case: `**REQUIRED PARAMETER**: action (must be specified in ALL calls)
|
|
148
|
+
|
|
149
|
+
Use Case Catalog - Browse and search complete workflow scenarios
|
|
150
|
+
|
|
151
|
+
Actions:
|
|
152
|
+
- get: Get complete use case workflow by ID
|
|
153
|
+
- search: Search use cases by keyword/category
|
|
154
|
+
- list_all: List all use cases with filtering and pagination
|
|
155
|
+
|
|
156
156
|
Use this tool to learn end-to-end workflows and multi-step operations. Returns workflow steps with executable code examples.`,
|
|
157
157
|
suggest: 'Intelligent decision/constraint suggestion system. Find related decisions by key pattern, tags, or full context. Prevents duplicates and ensures consistency.',
|
|
158
158
|
queue: 'Hook queue management - list, clear, remove pending items from .sqlew/queue/pending.json. Use action: "help" for documentation.',
|
|
@@ -103,11 +103,11 @@ export async function teardownDatabase(db) {
|
|
|
103
103
|
async function dropAllTables(db, dbType) {
|
|
104
104
|
if (dbType === 'mysql' || dbType === 'mariadb') {
|
|
105
105
|
await db.raw('SET FOREIGN_KEY_CHECKS=0');
|
|
106
|
-
const tables = await db.raw(`
|
|
107
|
-
SELECT TABLE_NAME
|
|
108
|
-
FROM INFORMATION_SCHEMA.TABLES
|
|
109
|
-
WHERE TABLE_SCHEMA = DATABASE()
|
|
110
|
-
AND TABLE_TYPE = 'BASE TABLE'
|
|
106
|
+
const tables = await db.raw(`
|
|
107
|
+
SELECT TABLE_NAME
|
|
108
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
109
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
110
|
+
AND TABLE_TYPE = 'BASE TABLE'
|
|
111
111
|
`);
|
|
112
112
|
for (const row of tables[0]) {
|
|
113
113
|
await db.raw(`DROP TABLE IF EXISTS \`${row.TABLE_NAME}\``);
|
|
@@ -115,10 +115,10 @@ async function dropAllTables(db, dbType) {
|
|
|
115
115
|
await db.raw('SET FOREIGN_KEY_CHECKS=1');
|
|
116
116
|
}
|
|
117
117
|
else if (dbType === 'postgresql') {
|
|
118
|
-
const tables = await db.raw(`
|
|
119
|
-
SELECT tablename
|
|
120
|
-
FROM pg_tables
|
|
121
|
-
WHERE schemaname = 'public'
|
|
118
|
+
const tables = await db.raw(`
|
|
119
|
+
SELECT tablename
|
|
120
|
+
FROM pg_tables
|
|
121
|
+
WHERE schemaname = 'public'
|
|
122
122
|
`);
|
|
123
123
|
for (const row of tables.rows) {
|
|
124
124
|
await db.raw(`DROP TABLE IF EXISTS "${row.tablename}" CASCADE`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-hook-normalization.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/unit/hooks/codex-hook-normalization.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex Hook Normalization Unit Tests
|
|
3
|
+
*
|
|
4
|
+
* @since v5.2.1
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { normalizeHookInput, isPlanMode, } from '../../../cli/hooks/stdin-parser.js';
|
|
11
|
+
import { findCodexTranscriptPath, extractPlanMarkdownFromCodexTranscript, } from '../../../cli/hooks/codex-transcript.js';
|
|
12
|
+
import { determineProjectRoot } from '../../../utils/project-root.js';
|
|
13
|
+
describe('codex-hook-normalization', () => {
|
|
14
|
+
const originalEnv = { ...process.env };
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
delete process.env.CODEX_SESSION_ID;
|
|
17
|
+
delete process.env.CODEX_CWD;
|
|
18
|
+
delete process.env.CODEX_HOME;
|
|
19
|
+
delete process.env.GROK_HOOK_EVENT;
|
|
20
|
+
delete process.env.GROK_WORKSPACE_ROOT;
|
|
21
|
+
delete process.env.CLAUDE_PROJECT_DIR;
|
|
22
|
+
delete process.env.SQLEW_PROJECT_ROOT;
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
process.env = { ...originalEnv };
|
|
26
|
+
});
|
|
27
|
+
describe('normalizeHookInput', () => {
|
|
28
|
+
it('should normalize Codex shell_command to Bash', () => {
|
|
29
|
+
const result = normalizeHookInput({
|
|
30
|
+
hook_event_name: 'PreToolUse',
|
|
31
|
+
tool_name: 'shell_command',
|
|
32
|
+
cwd: 'C:/project',
|
|
33
|
+
});
|
|
34
|
+
assert.strictEqual(result.client, 'codex');
|
|
35
|
+
assert.strictEqual(result.tool_name, 'Bash');
|
|
36
|
+
assert.strictEqual(result.cwd, 'C:/project');
|
|
37
|
+
});
|
|
38
|
+
it('should normalize Codex apply_patch to Edit', () => {
|
|
39
|
+
const result = normalizeHookInput({
|
|
40
|
+
hook_event_name: 'PostToolUse',
|
|
41
|
+
tool_name: 'apply_patch',
|
|
42
|
+
cwd: 'C:/project',
|
|
43
|
+
});
|
|
44
|
+
assert.strictEqual(result.client, 'codex');
|
|
45
|
+
assert.strictEqual(result.tool_name, 'Edit');
|
|
46
|
+
});
|
|
47
|
+
it('should map collaboration_mode plan to permission_mode plan', () => {
|
|
48
|
+
const result = normalizeHookInput({
|
|
49
|
+
hook_event_name: 'UserPromptSubmit',
|
|
50
|
+
collaboration_mode: { mode: 'plan' },
|
|
51
|
+
cwd: 'C:/project',
|
|
52
|
+
session_id: 'sess-codex-1',
|
|
53
|
+
});
|
|
54
|
+
assert.strictEqual(result.client, 'codex');
|
|
55
|
+
assert.strictEqual(result.collaboration_mode, 'plan');
|
|
56
|
+
assert.strictEqual(result.permission_mode, 'plan');
|
|
57
|
+
});
|
|
58
|
+
it('should use CODEX env vars when stdin is empty', () => {
|
|
59
|
+
process.env.CODEX_SESSION_ID = 'sess-1';
|
|
60
|
+
process.env.CODEX_CWD = 'C:/project';
|
|
61
|
+
const result = normalizeHookInput({});
|
|
62
|
+
assert.strictEqual(result.client, 'codex');
|
|
63
|
+
assert.strictEqual(result.session_id, 'sess-1');
|
|
64
|
+
assert.strictEqual(result.cwd, 'C:/project');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('isPlanMode', () => {
|
|
68
|
+
it('should detect Codex plan mode via collaboration_mode', () => {
|
|
69
|
+
assert.strictEqual(isPlanMode({ client: 'codex', collaboration_mode: 'plan' }), true);
|
|
70
|
+
});
|
|
71
|
+
it('should detect Codex after normalization', () => {
|
|
72
|
+
const normalized = normalizeHookInput({
|
|
73
|
+
hook_event_name: 'UserPromptSubmit',
|
|
74
|
+
collaboration_mode_kind: 'plan',
|
|
75
|
+
cwd: 'C:/project',
|
|
76
|
+
});
|
|
77
|
+
assert.strictEqual(isPlanMode(normalized), true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('findCodexTranscriptPath', () => {
|
|
81
|
+
it('should reject invalid session ids', () => {
|
|
82
|
+
assert.strictEqual(findCodexTranscriptPath('../escape'), null);
|
|
83
|
+
assert.strictEqual(findCodexTranscriptPath(''), null);
|
|
84
|
+
});
|
|
85
|
+
it('should find an existing local rollout transcript when present', () => {
|
|
86
|
+
const sessionId = '019e85cb-a131-7760-9a30-f164fcf83073';
|
|
87
|
+
const expected = join(homedir(), '.codex', 'sessions', '2026', '06', '02', `rollout-2026-06-02T09-46-15-${sessionId}.jsonl`);
|
|
88
|
+
const found = findCodexTranscriptPath(sessionId);
|
|
89
|
+
if (found) {
|
|
90
|
+
assert.strictEqual(found, expected);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('extractPlanMarkdownFromCodexTranscript', () => {
|
|
95
|
+
it('should return null for missing files', () => {
|
|
96
|
+
assert.strictEqual(extractPlanMarkdownFromCodexTranscript('C:/missing/transcript.jsonl'), null);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('determineProjectRoot', () => {
|
|
100
|
+
// determineProjectRoot uses path.isAbsolute(), which is platform-specific:
|
|
101
|
+
// 'C:/...' is absolute only on win32. Build paths that are absolute on the
|
|
102
|
+
// current platform so the precedence assertions hold on Linux CI too.
|
|
103
|
+
const absRoot = (p) => (process.platform === 'win32' ? `C:${p}` : p);
|
|
104
|
+
it('should prefer CODEX_CWD over SQLEW_PROJECT_ROOT', () => {
|
|
105
|
+
const codexRoot = absRoot('/codex-workspace');
|
|
106
|
+
process.env.CODEX_CWD = codexRoot;
|
|
107
|
+
process.env.SQLEW_PROJECT_ROOT = absRoot('/other');
|
|
108
|
+
assert.strictEqual(determineProjectRoot({}), codexRoot);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=codex-hook-normalization.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-hook-normalization.test.js","sourceRoot":"","sources":["../../../../src/tests/unit/hooks/codex-hook-normalization.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok-hook-normalization.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/unit/hooks/grok-hook-normalization.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grok Build Hook Normalization Unit Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests normalizeHookInput() and computeGrokPlanPath() for Grok Build compatibility.
|
|
5
|
+
*
|
|
6
|
+
* @since v5.2.0
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
9
|
+
import assert from 'node:assert';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { normalizeHookInput, computeGrokPlanPath, isPlanMode, } from '../../../cli/hooks/stdin-parser.js';
|
|
13
|
+
import { determineProjectRoot } from '../../../utils/project-root.js';
|
|
14
|
+
describe('grok-hook-normalization', () => {
|
|
15
|
+
const originalEnv = { ...process.env };
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
delete process.env.GROK_HOOK_EVENT;
|
|
18
|
+
delete process.env.GROK_WORKSPACE_ROOT;
|
|
19
|
+
delete process.env.GROK_SESSION_ID;
|
|
20
|
+
delete process.env.CLAUDE_PROJECT_DIR;
|
|
21
|
+
delete process.env.SQLEW_PROJECT_ROOT;
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
process.env = { ...originalEnv };
|
|
25
|
+
});
|
|
26
|
+
describe('normalizeHookInput', () => {
|
|
27
|
+
it('should pass through Claude payloads unchanged', () => {
|
|
28
|
+
const input = {
|
|
29
|
+
hook_event_name: 'PostToolUse',
|
|
30
|
+
tool_name: 'ExitPlanMode',
|
|
31
|
+
cwd: 'C:/project',
|
|
32
|
+
session_id: 'abc-123',
|
|
33
|
+
};
|
|
34
|
+
const result = normalizeHookInput(input);
|
|
35
|
+
assert.strictEqual(result.hook_event_name, 'PostToolUse');
|
|
36
|
+
assert.strictEqual(result.tool_name, 'ExitPlanMode');
|
|
37
|
+
assert.strictEqual(result.client, undefined);
|
|
38
|
+
});
|
|
39
|
+
it('should normalize Grok camelCase payload to Claude shape', () => {
|
|
40
|
+
const result = normalizeHookInput({
|
|
41
|
+
hookEventName: 'post_tool_use',
|
|
42
|
+
toolName: 'exit_plan_mode',
|
|
43
|
+
sessionId: '019eb585-d6ec-7ae0-992a-d60340763a20',
|
|
44
|
+
workspaceRoot: 'C:\\Users\\kitayama\\RustroverProjects\\mcp-sqlew',
|
|
45
|
+
});
|
|
46
|
+
assert.strictEqual(result.client, 'grok');
|
|
47
|
+
assert.strictEqual(result.hook_event_name, 'PostToolUse');
|
|
48
|
+
assert.strictEqual(result.tool_name, 'ExitPlanMode');
|
|
49
|
+
assert.strictEqual(result.session_id, '019eb585-d6ec-7ae0-992a-d60340763a20');
|
|
50
|
+
assert.strictEqual(result.cwd, 'C:\\Users\\kitayama\\RustroverProjects\\mcp-sqlew');
|
|
51
|
+
});
|
|
52
|
+
it('should map enter_plan_mode PreToolUse event', () => {
|
|
53
|
+
const result = normalizeHookInput({
|
|
54
|
+
hookEventName: 'pre_tool_use',
|
|
55
|
+
toolName: 'enter_plan_mode',
|
|
56
|
+
workspaceRoot: 'C:/project',
|
|
57
|
+
});
|
|
58
|
+
assert.strictEqual(result.hook_event_name, 'PreToolUse');
|
|
59
|
+
assert.strictEqual(result.tool_name, 'EnterPlanMode');
|
|
60
|
+
});
|
|
61
|
+
it('should use env vars when stdin is empty', () => {
|
|
62
|
+
process.env.GROK_HOOK_EVENT = 'post_tool_use';
|
|
63
|
+
process.env.GROK_WORKSPACE_ROOT = 'C:/project';
|
|
64
|
+
process.env.GROK_SESSION_ID = 'sess-1';
|
|
65
|
+
const result = normalizeHookInput({});
|
|
66
|
+
assert.strictEqual(result.client, 'grok');
|
|
67
|
+
assert.strictEqual(result.hook_event_name, 'PostToolUse');
|
|
68
|
+
assert.strictEqual(result.cwd, 'C:/project');
|
|
69
|
+
assert.strictEqual(result.session_id, 'sess-1');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('isPlanMode', () => {
|
|
73
|
+
it('should detect Claude plan mode via permission_mode', () => {
|
|
74
|
+
assert.strictEqual(isPlanMode({ hook_event_name: 'UserPromptSubmit', permission_mode: 'plan' }), true);
|
|
75
|
+
});
|
|
76
|
+
it('should return false for Claude non-plan prompts', () => {
|
|
77
|
+
assert.strictEqual(isPlanMode({ hook_event_name: 'UserPromptSubmit', permission_mode: 'default' }), false);
|
|
78
|
+
});
|
|
79
|
+
it('should detect Grok plan mode after normalization (enter_plan_mode)', () => {
|
|
80
|
+
// Regression: normalizeHookInput maps enter_plan_mode -> EnterPlanMode,
|
|
81
|
+
// so isPlanMode must match the normalized PascalCase name.
|
|
82
|
+
const normalized = normalizeHookInput({
|
|
83
|
+
hookEventName: 'pre_tool_use',
|
|
84
|
+
toolName: 'enter_plan_mode',
|
|
85
|
+
workspaceRoot: 'C:/project',
|
|
86
|
+
});
|
|
87
|
+
assert.strictEqual(isPlanMode(normalized), true);
|
|
88
|
+
});
|
|
89
|
+
it('should detect Grok plan mode after normalization (exit_plan_mode)', () => {
|
|
90
|
+
const normalized = normalizeHookInput({
|
|
91
|
+
hookEventName: 'post_tool_use',
|
|
92
|
+
toolName: 'exit_plan_mode',
|
|
93
|
+
workspaceRoot: 'C:/project',
|
|
94
|
+
});
|
|
95
|
+
assert.strictEqual(isPlanMode(normalized), true);
|
|
96
|
+
});
|
|
97
|
+
it('should accept raw snake_case tool names defensively', () => {
|
|
98
|
+
assert.strictEqual(isPlanMode({ tool_name: 'enter_plan_mode' }), true);
|
|
99
|
+
});
|
|
100
|
+
it('should return false for unrelated tools', () => {
|
|
101
|
+
assert.strictEqual(isPlanMode({ tool_name: 'Bash' }), false);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('computeGrokPlanPath', () => {
|
|
105
|
+
it('should encode Windows workspace path for session directory', () => {
|
|
106
|
+
const workspace = 'C:\\Users\\kitayama\\RustroverProjects\\mcp-sqlew';
|
|
107
|
+
const sessionId = '019eb585-d6ec-7ae0-992a-d60340763a20';
|
|
108
|
+
const planPath = computeGrokPlanPath(workspace, sessionId);
|
|
109
|
+
const encoded = encodeURIComponent(workspace);
|
|
110
|
+
assert.strictEqual(planPath, join(homedir(), '.grok', 'sessions', encoded, sessionId, 'plan.md'));
|
|
111
|
+
});
|
|
112
|
+
it('should reject path traversal in sessionId', () => {
|
|
113
|
+
assert.strictEqual(computeGrokPlanPath('C:/project', '../escape'), null);
|
|
114
|
+
assert.strictEqual(computeGrokPlanPath('C:/project', ''), null);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('determineProjectRoot', () => {
|
|
118
|
+
// determineProjectRoot uses path.isAbsolute(), which is platform-specific:
|
|
119
|
+
// 'C:/...' is absolute only on win32. Build paths that are absolute on the
|
|
120
|
+
// current platform so the precedence assertions hold on Linux CI too.
|
|
121
|
+
const absRoot = (p) => (process.platform === 'win32' ? `C:${p}` : p);
|
|
122
|
+
it('should prefer GROK_WORKSPACE_ROOT over SQLEW_PROJECT_ROOT', () => {
|
|
123
|
+
const grokRoot = absRoot('/grok-workspace');
|
|
124
|
+
process.env.GROK_WORKSPACE_ROOT = grokRoot;
|
|
125
|
+
process.env.SQLEW_PROJECT_ROOT = absRoot('/other');
|
|
126
|
+
assert.strictEqual(determineProjectRoot({}), grokRoot);
|
|
127
|
+
});
|
|
128
|
+
it('should prefer CLAUDE_PROJECT_DIR over GROK_WORKSPACE_ROOT', () => {
|
|
129
|
+
const claudeRoot = absRoot('/claude-project');
|
|
130
|
+
process.env.CLAUDE_PROJECT_DIR = claudeRoot;
|
|
131
|
+
process.env.GROK_WORKSPACE_ROOT = absRoot('/grok-workspace');
|
|
132
|
+
assert.strictEqual(determineProjectRoot({}), claudeRoot);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
//# sourceMappingURL=grok-hook-normalization.test.js.map
|