getprismo 0.1.17 → 0.1.19

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/README.md CHANGED
@@ -52,6 +52,7 @@ agent-native npx getprismo mcp
52
52
  - repeated file reads (same file loaded 100+ times in one session)
53
53
  - repeated commands (agent running the same command in a loop)
54
54
  - high context risk sessions that should have been split at task boundaries
55
+ - session-derived ignore candidates from actual Claude/Codex logs (`logs/debug.log`, `dist/app.js`, `package-lock.json`, source-stream dumps)
55
56
 
56
57
  ---
57
58
 
@@ -96,6 +97,10 @@ Next:
96
97
 
97
98
  doctor went from 79 to 91 in one run. the repo now has proper ignore files, compact context packs, and a clear starting point for the next coding session.
98
99
 
100
+ `scan --usage` and `doctor` can also turn real session leaks into concrete ignore suggestions. if local Claude/Codex logs show `logs/debug.log`, `dist/app.js`, `package-lock.json`, source-stream dumps, or other noisy files repeatedly entering context, prismodev adds conservative `.claudeignore` / `.cursorignore` candidate rules instead of only reporting the problem.
101
+
102
+ when you want PrismoDev to apply those ignore suggestions directly, use `npx getprismo doctor --apply-suggestions`. it appends only missing rules, writes `.claudeignore.prismo-backup` / `.cursorignore.prismo-backup` first, and still does not touch `CLAUDE.md`, `AGENTS.md`, `.gitignore`, or source code.
103
+
99
104
  ---
100
105
 
101
106
  ## real output: watch
@@ -559,6 +564,7 @@ npx getprismo doctor # full run
559
564
  npx getprismo firewall auth-bug # generate scoped context firewall
560
565
  npx getprismo doctor --dry-run # preview without writing files
561
566
  npx getprismo doctor --apply-ignores-only # only create ignore files
567
+ npx getprismo doctor --apply-suggestions # append missing ignore suggestions with backups
562
568
  npx getprismo doctor --no-context-packs # skip .prismo/ generation
563
569
  npx getprismo doctor frontend # scope to frontend
564
570
  npx getprismo doctor --json # machine-readable output
package/docs/live-demo.md CHANGED
@@ -6,12 +6,15 @@ Use this flow to show the full product loop on a real repo.
6
6
 
7
7
  ```bash
8
8
  npx getprismo doctor
9
+ npx getprismo doctor --apply-suggestions
9
10
  ```
10
11
 
11
12
  Shows:
12
13
 
13
14
  - before/after repo score
14
15
  - missing `.claudeignore` / `.cursorignore`
16
+ - ignore suggestions derived from actual local Claude/Codex session leaks
17
+ - optional safe append mode for missing ignore rules with backups
15
18
  - generated artifacts exposed to AI context
16
19
  - compact `.prismo/` context packs
17
20
  - recommended next starting context
@@ -4,6 +4,7 @@ module.exports = function createDoctor(deps) {
4
4
  path,
5
5
  NPX_COMMAND,
6
6
  color,
7
+ appendIgnoreSuggestions,
7
8
  applyFixes,
8
9
  calculateReductionPercent,
9
10
  chooseRecommendedScope,
@@ -169,8 +170,11 @@ function runDoctor(rootDir = process.cwd(), options = {}) {
169
170
 
170
171
  const fixDone = printStep(options.dryRun ? "Planning safe AI-context fixes" : "Applying safe AI-context fixes", options.json);
171
172
  const applyIgnoresOnly = Boolean(options.applyIgnoresOnly);
173
+ const applySuggestions = Boolean(options.applySuggestions);
172
174
  const noContextPacks = Boolean(options.noContextPacks || applyIgnoresOnly);
173
- const fixActions = applyFixes(before, { dryRun: options.dryRun, ignoresOnly: applyIgnoresOnly });
175
+ const fixActions = applySuggestions
176
+ ? appendIgnoreSuggestions(before, { dryRun: options.dryRun })
177
+ : applyFixes(before, { dryRun: options.dryRun, ignoresOnly: applyIgnoresOnly });
174
178
  fixDone();
175
179
 
176
180
  const initialContext = createOptimizeContext(root);
@@ -220,6 +224,7 @@ function runDoctor(rootDir = process.cwd(), options = {}) {
220
224
  ])),
221
225
  dryRun: Boolean(options.dryRun),
222
226
  applyIgnoresOnly,
227
+ applySuggestions,
223
228
  noContextPacks,
224
229
  generatedAt: new Date().toISOString(),
225
230
  };
@@ -355,6 +360,7 @@ function toDoctorJsonPayload(result) {
355
360
  nextCommands: result.nextCommands,
356
361
  dryRun: result.dryRun,
357
362
  applyIgnoresOnly: result.applyIgnoresOnly,
363
+ applySuggestions: result.applySuggestions,
358
364
  noContextPacks: result.noContextPacks,
359
365
  generatedAt: result.generatedAt,
360
366
  };
@@ -379,7 +385,7 @@ function renderDoctorTerminal(result) {
379
385
  lines.push(`Payoff: ${scoreDelta > 0 ? `repo is ${scoreDelta} points cleaner for AI coding sessions` : "safe fixes applied; remaining risk needs manual cleanup"}`);
380
386
  }
381
387
  lines.push("");
382
- lines.push(result.dryRun ? "Would Fix:" : "Fixed:");
388
+ lines.push(result.applySuggestions ? (result.dryRun ? "Would Apply Suggestions:" : "Applied Suggestions:") : (result.dryRun ? "Would Fix:" : "Fixed:"));
383
389
  if (result.fixActions.length) result.fixActions.forEach((action) => lines.push(`- ${action}`));
384
390
  else lines.push("- No safe fix files needed");
385
391
  if (result.noContextPacks) {
@@ -410,7 +416,9 @@ function renderDoctorTerminal(result) {
410
416
  lines.push(`${NPX_COMMAND} watch --auto`);
411
417
  lines.push("Tell your agent: Follow .prismo/live-guardrails.md during this session.");
412
418
  lines.push("");
413
- lines.push("Doctor only creates safe recommendation/context files. It does not overwrite CLAUDE.md, AGENTS.md, .gitignore, or source code.");
419
+ lines.push(result.applySuggestions
420
+ ? "Doctor only appends missing ignore rules and writes backups. It does not overwrite CLAUDE.md, AGENTS.md, .gitignore, or source code."
421
+ : "Doctor only creates safe recommendation/context files. It does not overwrite CLAUDE.md, AGENTS.md, .gitignore, or source code.");
414
422
  return lines.join("\n");
415
423
  }
416
424
 
@@ -49,6 +49,70 @@ function renderAgentsRecommendations(result) {
49
49
  ].join("\n");
50
50
  }
51
51
 
52
+ function readIgnoreLines(filePath) {
53
+ if (!fs.existsSync(filePath)) return [];
54
+ return fs.readFileSync(filePath, "utf8").split(/\r?\n/);
55
+ }
56
+
57
+ function normalizeRule(line) {
58
+ return String(line || "").trim();
59
+ }
60
+
61
+ function stableBackup(filePath) {
62
+ if (!fs.existsSync(filePath)) return null;
63
+ const backupPath = `${filePath}.prismo-backup`;
64
+ if (fs.existsSync(backupPath)) backupIfExists(backupPath);
65
+ fs.copyFileSync(filePath, backupPath);
66
+ return backupPath;
67
+ }
68
+
69
+ function appendIgnoreSuggestions(result, options = {}) {
70
+ const actions = [];
71
+ const files = [
72
+ {
73
+ name: ".claudeignore",
74
+ path: path.join(result.root, ".claudeignore"),
75
+ suggestions: result.hasClaudeIgnore ? result.missingClaudeIgnoreSuggestions || [] : result.recommendedClaudeIgnore || [],
76
+ },
77
+ {
78
+ name: ".cursorignore",
79
+ path: path.join(result.root, ".cursorignore"),
80
+ suggestions: result.hasCursorIgnore ? result.missingCursorIgnoreSuggestions || [] : result.recommendedCursorIgnore || [],
81
+ },
82
+ ];
83
+
84
+ for (const file of files) {
85
+ const existingLines = readIgnoreLines(file.path);
86
+ const existingRules = new Set(existingLines.map(normalizeRule).filter((line) => line && !line.startsWith("#")));
87
+ const missing = Array.from(new Set((file.suggestions || []).map(normalizeRule).filter(Boolean)))
88
+ .filter((rule) => !existingRules.has(rule));
89
+
90
+ if (!missing.length) {
91
+ actions.push(`Skipped ${file.name}; existing rules already cover Prismo suggestions`);
92
+ continue;
93
+ }
94
+
95
+ if (options.dryRun) {
96
+ actions.push(`Would update ${file.name}: +${missing.join(", +")}`);
97
+ continue;
98
+ }
99
+
100
+ const existed = fs.existsSync(file.path);
101
+ const backupPath = stableBackup(file.path);
102
+ const nextLines = existingLines.slice();
103
+ while (nextLines.length && !nextLines[nextLines.length - 1].trim()) nextLines.pop();
104
+ if (nextLines.length) nextLines.push("");
105
+ nextLines.push("# Added by PrismoDev");
106
+ missing.forEach((rule) => nextLines.push(rule));
107
+ fs.writeFileSync(file.path, `${nextLines.join("\n")}\n`, "utf8");
108
+
109
+ actions.push(`${existed ? "Updated" : "Created"} ${file.name}: +${missing.join(", +")}`);
110
+ if (backupPath) actions.push(`Backup written: ${path.basename(backupPath)}`);
111
+ }
112
+
113
+ return actions;
114
+ }
115
+
52
116
  function applyFixes(result, options = {}) {
53
117
  const actions = [];
54
118
  const dryRunPrefix = options.dryRun ? "Would create" : "Created";
@@ -110,6 +174,7 @@ function applyFixes(result, options = {}) {
110
174
  }
111
175
 
112
176
  return {
177
+ appendIgnoreSuggestions,
113
178
  applyFixes,
114
179
  renderAgentsRecommendations,
115
180
  renderClaudeTemplate,
@@ -277,6 +277,16 @@ function renderMarkdownReport(result) {
277
277
  });
278
278
  lines.push("");
279
279
  }
280
+ if (result.sessionIgnoreSuggestions && result.sessionIgnoreSuggestions.length) {
281
+ lines.push("## Session-Derived Ignore Suggestions");
282
+ lines.push("");
283
+ lines.push("These rules came from local Claude/Codex session logs where noisy paths already entered context.");
284
+ lines.push("");
285
+ result.sessionIgnoreSuggestions.slice(0, 20).forEach((item) => {
286
+ lines.push(`- \`${item.pattern}\` - ${item.reason} (${item.count} mention${item.count === 1 ? "" : "s"}${item.examples.length ? `; e.g. ${item.examples[0]}` : ""})`);
287
+ });
288
+ lines.push("");
289
+ }
280
290
  lines.push("## Issues");
281
291
  lines.push("");
282
292
  if (!result.issues.length) {
@@ -97,6 +97,128 @@ function missingIgnoreSuggestions(recommended, existingPatterns) {
97
97
  return recommended.filter((pattern) => !ignoreSuggestionCovered(pattern, existingPatterns));
98
98
  }
99
99
 
100
+ const SESSION_NOISE_DIRS = new Set([
101
+ ".next",
102
+ ".nuxt",
103
+ ".prismo",
104
+ ".pytest_cache",
105
+ ".turbo",
106
+ "__pycache__",
107
+ "build",
108
+ "calendar-dumps",
109
+ "coverage",
110
+ "dist",
111
+ "event-dumps",
112
+ "events",
113
+ "exports",
114
+ "htmlcov",
115
+ "inbox-dumps",
116
+ "logs",
117
+ "models",
118
+ "node_modules",
119
+ "out",
120
+ "playwright-report",
121
+ "session-dumps",
122
+ "source-streams",
123
+ "state-backups",
124
+ "test-results",
125
+ "tmp",
126
+ "temp",
127
+ ]);
128
+
129
+ const SESSION_NOISE_FILE_NAMES = new Set([
130
+ "package-lock.json",
131
+ "pnpm-lock.yaml",
132
+ "yarn.lock",
133
+ "bun.lockb",
134
+ "coverage-final.json",
135
+ "lcov.info",
136
+ ]);
137
+
138
+ const SESSION_NOISE_EXTENSIONS = new Set([
139
+ ".db",
140
+ ".jsonl",
141
+ ".lock",
142
+ ".log",
143
+ ".sqlite",
144
+ ".sqlite3",
145
+ ]);
146
+
147
+ function cleanSessionPath(value) {
148
+ const text = String(value || "").trim().replace(/\\/g, "/");
149
+ if (!text || /^https?:\/\//.test(text)) return null;
150
+ const withoutQuotes = text.replace(/^["'`]+|["'`.,:;)\]]+$/g, "");
151
+ if (!withoutQuotes || withoutQuotes.includes("\n")) return null;
152
+ const markerIndex = withoutQuotes.indexOf("/Users/");
153
+ if (markerIndex > 0) return withoutQuotes.slice(markerIndex);
154
+ return withoutQuotes;
155
+ }
156
+
157
+ function sessionIgnorePatternForPath(value, root) {
158
+ const cleaned = cleanSessionPath(value);
159
+ if (!cleaned) return null;
160
+ const rootNormalized = normalizeRel(root);
161
+ let rel = cleaned;
162
+ if (path.isAbsolute(cleaned)) {
163
+ const normalized = normalizeRel(cleaned);
164
+ if (!normalized.startsWith(`${rootNormalized}/`)) return null;
165
+ rel = normalizeRel(path.relative(root, cleaned));
166
+ }
167
+ rel = normalizeRel(rel).replace(/^\.\//, "");
168
+ if (!rel || rel === "." || rel.startsWith("../") || rel.includes("..")) return null;
169
+
170
+ const segments = rel.split("/").filter(Boolean);
171
+ if (!segments.length) return null;
172
+ for (let index = 0; index < segments.length; index += 1) {
173
+ const segment = segments[index];
174
+ if (SESSION_NOISE_DIRS.has(segment)) {
175
+ return `${segments.slice(0, index + 1).join("/")}/`;
176
+ }
177
+ }
178
+
179
+ const fileName = segments[segments.length - 1];
180
+ const lowerName = fileName.toLowerCase();
181
+ const ext = path.extname(lowerName);
182
+ if (SESSION_NOISE_FILE_NAMES.has(lowerName)) return fileName;
183
+ if (SESSION_NOISE_EXTENSIONS.has(ext)) return rel;
184
+ if (/_state\.json$/i.test(fileName)) return "*_state.json";
185
+ if (/_tokens\.json$/i.test(fileName)) return "*_tokens.json";
186
+ if (/_export\.json$/i.test(fileName)) return "*_export.json";
187
+ if (/secret|credential|token/i.test(fileName) && /\.json$/i.test(fileName)) return rel;
188
+ return null;
189
+ }
190
+
191
+ function buildSessionIgnoreSuggestions(realUsage, root) {
192
+ if (!realUsage || !Array.isArray(realUsage.sessions)) return [];
193
+ const byPattern = new Map();
194
+ const add = (pattern, item, source, reason) => {
195
+ if (!pattern) return;
196
+ const existing = byPattern.get(pattern) || {
197
+ pattern,
198
+ source,
199
+ reason,
200
+ count: 0,
201
+ examples: [],
202
+ };
203
+ existing.count += Number(item?.count || 1);
204
+ const example = item?.value || item?.path || pattern;
205
+ if (example && !existing.examples.includes(example) && existing.examples.length < 3) existing.examples.push(example);
206
+ byPattern.set(pattern, existing);
207
+ };
208
+
209
+ for (const session of realUsage.sessions) {
210
+ for (const item of session.generatedArtifacts || []) {
211
+ add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Generated artifact entered local session context.");
212
+ }
213
+ for (const item of session.repeatedPathMentions || []) {
214
+ add(sessionIgnorePatternForPath(item.value, root), item, session.tool || "session", "Noisy path appeared repeatedly in local session context.");
215
+ }
216
+ }
217
+ return Array.from(byPattern.values())
218
+ .sort((a, b) => b.count - a.count || a.pattern.localeCompare(b.pattern))
219
+ .slice(0, 25);
220
+ }
221
+
100
222
  function getFileKind(filePath) {
101
223
  const ext = path.extname(filePath).toLowerCase();
102
224
  const name = path.basename(filePath).toLowerCase();
@@ -775,6 +897,7 @@ function toJsonPayload(result) {
775
897
  optimizationStack: result.optimizationStack,
776
898
  toolOutputRisk: result.toolOutputRisk,
777
899
  operationalNoise: result.operationalNoise,
900
+ sessionIgnoreSuggestions: result.sessionIgnoreSuggestions || [],
778
901
  proxyTrackingReadiness: result.proxyTrackingReadiness,
779
902
  suggestedClaudeIgnore: result.recommendedClaudeIgnore,
780
903
  suggestedCursorIgnore: result.recommendedCursorIgnore,
@@ -1061,7 +1184,19 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
1061
1184
  }
1062
1185
 
1063
1186
  const realUsage = options.includeUsage ? getUsageSummary({ tool: options.usageTool || "all", cwd: root, limit: options.usageLimit || 5 }) : null;
1187
+ const sessionIgnoreSuggestions = buildSessionIgnoreSuggestions(realUsage, root);
1064
1188
  addRealUsageIssues(issues, realUsage);
1189
+ if (sessionIgnoreSuggestions.length) {
1190
+ addIssue(
1191
+ issues,
1192
+ "medium",
1193
+ "ignore_file",
1194
+ `${sessionIgnoreSuggestions.length} session-derived ignore suggestion${sessionIgnoreSuggestions.length === 1 ? "" : "s"}`,
1195
+ sessionIgnoreSuggestions.slice(0, 5).map((item) => `${item.pattern} (${item.count}x)`).join(", "),
1196
+ "Review the generated .claudeignore/.cursorignore suggestions from actual local session context.",
1197
+ "Likely avoidable token exposure: these paths already appeared in local coding-agent context."
1198
+ );
1199
+ }
1065
1200
  const optimizationStack = detectOptimizationStack(root, claudeConfig, codexConfig);
1066
1201
  const agentReadiness = detectAgentReadiness(root, claudeConfig, codexConfig, realUsage);
1067
1202
  const toolOutputRisk = detectToolOutputRisk({ exposedLargeFiles, exposedHighRiskDirs, highRiskDirs });
@@ -1145,11 +1280,13 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
1145
1280
  .filter((file) => file.size >= 1024 * 1024 || ["log", "json", "minified", "lock/generated"].includes(file.kind))
1146
1281
  .map((file) => file.path);
1147
1282
  const operationalNoiseSuggestions = operationalNoise.files.map((file) => file.path);
1283
+ const sessionIgnorePatterns = sessionIgnoreSuggestions.map((item) => item.pattern);
1148
1284
  const recommendedClaudeIgnore = Array.from(new Set([
1149
1285
  ...DEFAULT_CLAUDEIGNORE,
1150
1286
  ...gitignorePatterns.filter((line) => !line.startsWith("!")),
1151
1287
  ...largeFileSuggestions,
1152
1288
  ...operationalNoiseSuggestions,
1289
+ ...sessionIgnorePatterns,
1153
1290
  ]));
1154
1291
  const recommendedCursorIgnore = Array.from(new Set([
1155
1292
  ...recommendedClaudeIgnore,
@@ -1203,6 +1340,7 @@ function scanRepo(rootDir = process.cwd(), options = {}) {
1203
1340
  recommendedCursorIgnore,
1204
1341
  missingClaudeIgnoreSuggestions,
1205
1342
  missingCursorIgnoreSuggestions,
1343
+ sessionIgnoreSuggestions,
1206
1344
  topTokenLeaks: getTopTokenLeaks(issues),
1207
1345
  generatedAt: new Date().toISOString(),
1208
1346
  };
@@ -186,7 +186,7 @@ function looksLikeUsefulPath(relPath) {
186
186
  function extractMentionedPaths(text, cwd = "") {
187
187
  const found = new Set();
188
188
  const source = String(text || "");
189
- const pathPattern = /(?:^|[\s"'`])((?:\.{0,2}\/)?(?:[\w .@-]+\/)+[\w .@+-]+\.[A-Za-z0-9]{1,12})/g;
189
+ const pathPattern = /(?:^|[\s"'`])((?:\.{0,2}\/)?(?:[\w.@-]+\/)+[\w.@+-]+\.[A-Za-z0-9]{1,12})/g;
190
190
  const filePattern = /(?:^|[\s"'`])((?:package-lock\.json|pnpm-lock\.yaml|yarn\.lock|coverage-final\.json|tsconfig\.json|pyproject\.toml|requirements\.txt|README\.md|CLAUDE\.md|AGENTS\.md))/g;
191
191
  for (const pattern of [pathPattern, filePattern]) {
192
192
  let match;
@@ -209,7 +209,7 @@ const {
209
209
  getNextCommands,
210
210
  });
211
211
 
212
- const { applyFixes } = require("./prismo-dev/fixes")({
212
+ const { appendIgnoreSuggestions, applyFixes } = require("./prismo-dev/fixes")({
213
213
  fs,
214
214
  path,
215
215
  backupIfExists,
@@ -239,6 +239,7 @@ const {
239
239
  NPX_COMMAND,
240
240
  color,
241
241
  applyFixes,
242
+ appendIgnoreSuggestions,
242
243
  calculateReductionPercent,
243
244
  chooseRecommendedScope,
244
245
  createOptimizeContext,
@@ -296,7 +297,7 @@ Usage:
296
297
  prismo --version
297
298
  prismo dev [path]
298
299
  prismo init [--json] [--dry-run] [path]
299
- prismo doctor [--json] [--dry-run] [--apply-ignores-only] [--no-context-packs] [--limit N] [path]
300
+ prismo doctor [--json] [--dry-run] [--apply-ignores-only] [--apply-suggestions] [--no-context-packs] [--limit N] [path]
300
301
  prismo firewall [task] [--json] [--dry-run] [path]
301
302
  prismo shield [--json] [path] -- <command ...>
302
303
  prismo shield last [--json] [--limit N] [path]
@@ -339,6 +340,7 @@ Options:
339
340
  --interval N Refresh interval in seconds for watch mode.
340
341
  --dry-run Preview doctor/fix actions without writing files.
341
342
  --apply-ignores-only Only create/suggest AI ignore files in doctor mode.
343
+ --apply-suggestions Append missing recommended ignore rules with backups.
342
344
  --no-context-packs Skip .prismo context-pack generation in doctor mode.
343
345
  --report Write .prismo/watch-report.md in watch mode.
344
346
  --rescue Print a paste-ready live-session rescue prompt in watch mode.
@@ -481,7 +483,7 @@ Output:
481
483
  doctor: `PrismoDev Doctor
482
484
 
483
485
  Usage:
484
- prismo doctor [--json] [--dry-run] [--apply-ignores-only] [--no-context-packs] [--limit N] [path]
486
+ prismo doctor [--json] [--dry-run] [--apply-ignores-only] [--apply-suggestions] [--no-context-packs] [--limit N] [path]
485
487
 
486
488
  Flow:
487
489
  1. Scan repo and local usage.
@@ -490,7 +492,9 @@ Flow:
490
492
  4. Re-scan and show before/after score.
491
493
 
492
494
  Notes:
493
- Doctor creates safe recommendation/context files only. It does not overwrite CLAUDE.md, AGENTS.md, .gitignore, or source code.`,
495
+ Doctor creates safe recommendation/context files by default.
496
+ --apply-suggestions appends missing .claudeignore/.cursorignore rules with backups.
497
+ Doctor does not overwrite CLAUDE.md, AGENTS.md, .gitignore, or source code.`,
494
498
  firewall: `Prismo Context Firewall
495
499
 
496
500
  Usage:
@@ -656,6 +660,7 @@ async function runCli(argv) {
656
660
  scope,
657
661
  dryRun: rest.includes("--dry-run"),
658
662
  applyIgnoresOnly: rest.includes("--apply-ignores-only"),
663
+ applySuggestions: rest.includes("--apply-suggestions"),
659
664
  noContextPacks: rest.includes("--no-context-packs"),
660
665
  });
661
666
  if (json) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getprismo",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Local AI coding workflow scanner for Codex, Claude Code, Cursor, and token-waste diagnostics.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/shanirsh/prismodev#readme",