Skip to content

Commit 86c6b48

Browse files
authored
Merge pull request #113 from Resgrid/develop
Develop
2 parents 2758657 + 0da9949 commit 86c6b48

102 files changed

Lines changed: 15346 additions & 1418 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ Gemfile
4141

4242
expo-env.d.ts
4343
# @end expo-cli
44+
.dual-graph/

CLAUDE.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<!-- dgc-policy-v11 -->
2+
# Dual-Graph Context Policy
3+
4+
This project uses a local dual-graph MCP server for efficient context retrieval.
5+
6+
## MANDATORY: Always follow this order
7+
8+
1. **Call `graph_continue` first** — before any file exploration, grep, or code reading.
9+
10+
2. **If `graph_continue` returns `needs_project=true`**: call `graph_scan` with the
11+
current project directory (`pwd`). Do NOT ask the user.
12+
13+
3. **If `graph_continue` returns `skip=true`**: project has fewer than 5 files.
14+
Do NOT do broad or recursive exploration. Read only specific files if their names
15+
are mentioned, or ask the user what to work on.
16+
17+
4. **Read `recommended_files`** using `graph_read`**one call per file**.
18+
- `graph_read` accepts a single `file` parameter (string). Call it separately for each
19+
recommended file. Do NOT pass an array or batch multiple files into one call.
20+
- `recommended_files` may contain `file::symbol` entries (e.g. `src/auth.ts::handleLogin`).
21+
Pass them verbatim to `graph_read(file: "src/auth.ts::handleLogin")` — it reads only
22+
that symbol's lines, not the full file.
23+
- Example: if `recommended_files` is `["src/auth.ts::handleLogin", "src/db.ts"]`,
24+
call `graph_read(file: "src/auth.ts::handleLogin")` and `graph_read(file: "src/db.ts")`
25+
as two separate calls (they can be parallel).
26+
27+
5. **Check `confidence` and obey the caps strictly:**
28+
- `confidence=high` -> Stop. Do NOT grep or explore further.
29+
- `confidence=medium` -> If recommended files are insufficient, call `fallback_rg`
30+
at most `max_supplementary_greps` time(s) with specific terms, then `graph_read`
31+
at most `max_supplementary_files` additional file(s). Then stop.
32+
- `confidence=low` -> Call `fallback_rg` at most `max_supplementary_greps` time(s),
33+
then `graph_read` at most `max_supplementary_files` file(s). Then stop.
34+
35+
## Token Usage
36+
37+
A `token-counter` MCP is available for tracking live token usage.
38+
39+
- To check how many tokens a large file or text will cost **before** reading it:
40+
`count_tokens({text: "<content>"})`
41+
- To log actual usage after a task completes (if the user asks):
42+
`log_usage({input_tokens: <est>, output_tokens: <est>, description: "<task>"})`
43+
- To show the user their running session cost:
44+
`get_session_stats()`
45+
46+
Live dashboard URL is printed at startup next to "Token usage".
47+
48+
## Rules
49+
50+
- Do NOT use `rg`, `grep`, or bash file exploration before calling `graph_continue`.
51+
- Do NOT do broad/recursive exploration at any confidence level.
52+
- `max_supplementary_greps` and `max_supplementary_files` are hard caps - never exceed them.
53+
- Do NOT dump full chat history.
54+
- Do NOT call `graph_retrieve` more than once per turn.
55+
- After edits, call `graph_register_edit` with the changed files. Use `file::symbol` notation (e.g. `src/auth.ts::handleLogin`) when the edit targets a specific function, class, or hook.
56+
57+
## Context Store
58+
59+
Whenever you make a decision, identify a task, note a next step, fact, or blocker during a conversation, call `graph_add_memory`.
60+
61+
**To add an entry:**
62+
```
63+
graph_add_memory(type="decision|task|next|fact|blocker", content="one sentence max 15 words", tags=["topic"], files=["relevant/file.ts"])
64+
```
65+
66+
**Do NOT write context-store.json directly** — always use `graph_add_memory`. It applies pruning and keeps the store healthy.
67+
68+
**Rules:**
69+
- Only log things worth remembering across sessions (not every minor detail)
70+
- `content` must be under 15 words
71+
- `files` lists the files this decision/task relates to (can be empty)
72+
- Log immediately when the item arises — not at session end
73+
74+
## Session End
75+
76+
When the user signals they are done (e.g. "bye", "done", "wrap up", "end session"), proactively update `CONTEXT.md` in the project root with:
77+
- **Current Task**: one sentence on what was being worked on
78+
- **Key Decisions**: bullet list, max 3 items
79+
- **Next Steps**: bullet list, max 3 items
80+
81+
Keep `CONTEXT.md` under 20 lines total. Do NOT summarize the full conversation — only what's needed to resume next session.

NOTICE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Resgrid Dispatch
2+
Copyright 2026 Resgrid, LLC
3+
4+
This product includes software developed for the Resgrid project.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Web-safe mock for @gorhom/bottom-sheet.
3+
* The real package depends on react-native-reanimated worklets which are unavailable on web.
4+
* This mock respects index/-1 visibility, onChange callbacks, and ref methods
5+
* so modals open/close correctly on web.
6+
*/
7+
const React = require('react');
8+
const { View, StyleSheet, Pressable, Modal } = require('react-native');
9+
10+
const BottomSheet = React.forwardRef(function BottomSheet(props, ref) {
11+
const { index = -1, children, onChange, snapPoints, backdropComponent, enablePanDownToClose } = props;
12+
const [currentIndex, setCurrentIndex] = React.useState(index);
13+
14+
React.useEffect(() => {
15+
setCurrentIndex(index);
16+
}, [index]);
17+
18+
React.useImperativeHandle(ref, () => ({
19+
expand: () => {
20+
setCurrentIndex(0);
21+
if (onChange) onChange(0);
22+
},
23+
collapse: () => {
24+
setCurrentIndex(0);
25+
if (onChange) onChange(0);
26+
},
27+
close: () => {
28+
setCurrentIndex(-1);
29+
if (onChange) onChange(-1);
30+
},
31+
snapToIndex: (i) => {
32+
setCurrentIndex(i);
33+
if (onChange) onChange(i);
34+
},
35+
snapToPosition: () => {},
36+
forceClose: () => {
37+
setCurrentIndex(-1);
38+
if (onChange) onChange(-1);
39+
},
40+
}));
41+
42+
const isVisible = currentIndex >= 0;
43+
if (!isVisible) return null;
44+
45+
const handleBackdropPress = () => {
46+
if (enablePanDownToClose !== false) {
47+
setCurrentIndex(-1);
48+
if (onChange) onChange(-1);
49+
}
50+
};
51+
52+
return React.createElement(
53+
Modal,
54+
{ visible: true, transparent: true, animationType: 'slide', onRequestClose: handleBackdropPress },
55+
React.createElement(
56+
View,
57+
{ style: styles.overlay },
58+
React.createElement(Pressable, { style: styles.backdrop, onPress: handleBackdropPress }),
59+
React.createElement(
60+
View,
61+
{ style: styles.sheet },
62+
children
63+
)
64+
)
65+
);
66+
});
67+
68+
const BottomSheetView = function BottomSheetView(props) {
69+
return React.createElement(View, { style: props.style }, props.children);
70+
};
71+
72+
const BottomSheetScrollView = function BottomSheetScrollView(props) {
73+
const { ScrollView } = require('react-native');
74+
return React.createElement(ScrollView, props, props.children);
75+
};
76+
77+
const BottomSheetBackdrop = function BottomSheetBackdrop() {
78+
return null;
79+
};
80+
81+
const BottomSheetModalProvider = function BottomSheetModalProvider(props) {
82+
return props.children || null;
83+
};
84+
85+
const BottomSheetModal = React.forwardRef(function BottomSheetModal(props, ref) {
86+
return React.createElement(BottomSheet, Object.assign({}, props, { ref: ref }));
87+
});
88+
89+
const styles = StyleSheet.create({
90+
overlay: {
91+
flex: 1,
92+
justifyContent: 'flex-end',
93+
},
94+
backdrop: {
95+
...StyleSheet.absoluteFillObject,
96+
backgroundColor: 'rgba(0,0,0,0.4)',
97+
},
98+
sheet: {
99+
backgroundColor: '#fff',
100+
borderTopLeftRadius: 16,
101+
borderTopRightRadius: 16,
102+
maxHeight: '70%',
103+
minHeight: 200,
104+
overflow: 'hidden',
105+
},
106+
});
107+
108+
module.exports = {
109+
__esModule: true,
110+
default: BottomSheet,
111+
BottomSheet,
112+
BottomSheetView,
113+
BottomSheetScrollView,
114+
BottomSheetBackdrop,
115+
BottomSheetModalProvider,
116+
BottomSheetModal,
117+
};

metro.config.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
const _ = require('lodash');
44
const path = require('path');
55
const { getSentryExpoConfig } = require('@sentry/react-native/metro');
6-
const exclusionList = require('metro-config/src/defaults/exclusionList');
7-
//const { getDefaultConfig } = require('expo/metro-config');
8-
//const path = require('path');
96
const { withNativeWind } = require('nativewind/metro');
107

118
const config = getSentryExpoConfig(__dirname, {
@@ -14,9 +11,11 @@ const config = getSentryExpoConfig(__dirname, {
1411

1512
// Exclude electron directory from Metro bundler for Android/iOS
1613
// Electron files use Node.js APIs that don't exist in React Native
17-
config.resolver.blockList = exclusionList([
18-
/electron\/.*/,
19-
]);
14+
const existingBlockList = config.resolver.blockList;
15+
const extraBlocked = [/electron\/.*/];
16+
config.resolver.blockList = existingBlockList
17+
? [...(Array.isArray(existingBlockList) ? existingBlockList : [existingBlockList]), ...extraBlocked]
18+
: extraBlocked;
2019

2120
// 1. Watch all files within the monorepo
2221
// 2. Let Metro know where to resolve packages and in what order
@@ -48,6 +47,21 @@ config.resolver.resolveRequest = (context, moduleName, platform) => {
4847
};
4948
}
5049

50+
// @gorhom/bottom-sheet depends on reanimated worklets - not available on web
51+
if (moduleName === '@gorhom/bottom-sheet') {
52+
return {
53+
type: 'sourceFile',
54+
filePath: path.resolve(__dirname, '__mocks__/@gorhom/bottom-sheet.web.js'),
55+
};
56+
}
57+
58+
// NetInfo - not needed on web, use navigator.onLine instead
59+
if (moduleName === '@react-native-community/netinfo') {
60+
return {
61+
type: 'empty',
62+
};
63+
}
64+
5165
// expo-keep-awake mock for web
5266
if (moduleName === 'expo-keep-awake') {
5367
return {

0 commit comments

Comments
 (0)