scribeclock 0.1.5 → 0.1.7
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 +2 -0
- package/dist/commands/ingest.js +61 -1
- package/dist/commands/ingestAgentPrompt.js +155 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,8 @@ the app become tasks on the project; docs: https://scribeclock.com/help/bug-repo
|
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
73
|
scribeclock connect --env # links this repo to its project, writes .scribeclock.json + SCRIBECLOCK_INGEST_KEY to .env
|
|
74
|
+
scribeclock ingest prompt --platform ios | pbcopy # the prompt to paste into your AI agent: key, event shape, stack-specific steps
|
|
75
|
+
scribeclock issues diagnose 3f2a9c1b --comment # root-cause brief: where it died, likely causes, what to check, files to open
|
|
74
76
|
scribeclock ingest test # sends a test error straight to https://scribeclock.com/ingest → issue + task
|
|
75
77
|
scribeclock issues # what users hit: level, title, where, count, users, last seen, task
|
|
76
78
|
scribeclock issues show 3f2a9c1b # stack trace, breadcrumbs, trace logs, context
|
package/dist/commands/ingest.js
CHANGED
|
@@ -6,6 +6,7 @@ import { EXIT } from "../types.js";
|
|
|
6
6
|
import { fmtDate, relTime } from "../util/format.js";
|
|
7
7
|
import { PROJECT_FLAG, cmd, emit, guardedEmit, list, num, personName, projectArg, requirePositional, str, taskKey } from "./_shared.js";
|
|
8
8
|
import { parseDotenv } from "./ops.js";
|
|
9
|
+
import { AGENT_PROMPT_PLATFORMS, buildAgentPrompt } from "./ingestAgentPrompt.js";
|
|
9
10
|
export const INGEST_URL = "https://scribeclock.com/ingest";
|
|
10
11
|
export const INGEST_DOCS_URL = "https://scribeclock.com/help/bug-reports";
|
|
11
12
|
const KEY_ENV = "SCRIBECLOCK_INGEST_KEY";
|
|
@@ -476,6 +477,36 @@ async function issueStatusCommand(ctx, inv, status) {
|
|
|
476
477
|
ctx.out.info(ctx.out.dim("Tip: pass --release <version> (or --commit) so a later occurrence in another release counts as a regression."));
|
|
477
478
|
});
|
|
478
479
|
}
|
|
480
|
+
export const issuesDiagnose = cmd({
|
|
481
|
+
path: "issues diagnose",
|
|
482
|
+
group: "Bug reports",
|
|
483
|
+
summary: "Root-cause brief for an issue: where it died (app frames only), likely causes with evidence, what to check, files to open, the trail to reproduce.",
|
|
484
|
+
description: "Reads everything stored for the issue and prints a diagnosis: the app's own frames with reporter and runtime plumbing removed (Swift symbols demangled), the failure category and the most likely causes with the evidence and the exact checks for each, the files / functions to open, the breadcrumb trail to reproduce, impact by release and environment, the device and the last trace logs, similar open issues. --comment posts it on the issue's task; --model adds an analysis from the organization's configured AI provider.",
|
|
485
|
+
tool: "diagnose_issue",
|
|
486
|
+
positionals: [{ name: "issue", required: true, description: "Issue id (short or full) or a task key (#123)." }],
|
|
487
|
+
flags: [
|
|
488
|
+
{ name: "events", alias: "n", type: "number", description: "Recent events to read (default 10)." },
|
|
489
|
+
{ name: "comment", type: "boolean", description: "Post the brief as a comment on the linked task." },
|
|
490
|
+
{ name: "model", type: "boolean", description: "Also ask the organization's AI provider for a written analysis." },
|
|
491
|
+
],
|
|
492
|
+
examples: ["scribeclock issues diagnose 3f2a9c1b", "scribeclock issues diagnose '#731' --comment", "scribeclock issues diagnose 3f2a9c1b --json | jq .diagnosis.open_in_code"],
|
|
493
|
+
async run(ctx, inv) {
|
|
494
|
+
const ref = requirePositional(inv, 0, "An issue id", "scribeclock issues diagnose 3f2a9c1b");
|
|
495
|
+
const data = await ctx.call("diagnose_issue", { issue_id: ref, event_limit: num(inv.flags.events), post_comment: inv.flags.comment === true, with_model: inv.flags.model === true });
|
|
496
|
+
return emit(ctx, data, () => {
|
|
497
|
+
ctx.out.log(String(data.markdown ?? ""));
|
|
498
|
+
if (data.model_analysis) {
|
|
499
|
+
ctx.out.log("");
|
|
500
|
+
ctx.out.log(ctx.out.bold("Analysis from your AI provider"));
|
|
501
|
+
ctx.out.log(String(data.model_analysis));
|
|
502
|
+
}
|
|
503
|
+
else if (data.model_note)
|
|
504
|
+
ctx.out.info(ctx.out.dim(String(data.model_note)));
|
|
505
|
+
if (data.posted_comment)
|
|
506
|
+
ctx.out.info("Posted on the task.");
|
|
507
|
+
});
|
|
508
|
+
},
|
|
509
|
+
});
|
|
479
510
|
export const issuesResolve = cmd({
|
|
480
511
|
path: "issues resolve",
|
|
481
512
|
group: "Bug reports",
|
|
@@ -757,6 +788,35 @@ export const ingestSnippetsCommand = cmd({
|
|
|
757
788
|
return EXIT.OK;
|
|
758
789
|
},
|
|
759
790
|
});
|
|
791
|
+
export const ingestPromptCommand = cmd({
|
|
792
|
+
path: "ingest prompt",
|
|
793
|
+
group: "Bug reports",
|
|
794
|
+
summary: "Print the prompt to paste into an AI agent so it connects this repository: key, event shape and the exact steps for the stack.",
|
|
795
|
+
description: "The same prompt the project's Bug reports page offers. It embeds the ingest key (from --key, SCRIBECLOCK_INGEST_KEY or .env), the project and repository from .scribeclock.json, and stack-specific steps: the reporter file to write, crash hooks, bug reports, breadcrumbs, trace logs, sessions, screens, clicks, custom events, then a verified test event. Pipe it to the clipboard (pbcopy / xclip) or straight into the agent.",
|
|
796
|
+
tool: ["list_ingest_keys"],
|
|
797
|
+
flags: [
|
|
798
|
+
{ name: "platform", alias: "p", type: "string", choices: AGENT_PROMPT_PLATFORMS.map((o) => o.id), description: "Stack to write the steps for (default: auto — the agent detects it)." },
|
|
799
|
+
{ name: "key", alias: "k", type: "string", description: "Ingest key to embed (default: from the environment / .env; omitted → the agent fetches one itself)." },
|
|
800
|
+
{ name: "no-key", type: "boolean", description: "Never embed a key (the agent runs connect_repo / scribeclock connect)." },
|
|
801
|
+
],
|
|
802
|
+
examples: ["scribeclock ingest prompt --platform ios | pbcopy", "scribeclock ingest prompt --no-key", "scribeclock connect --env && scribeclock ingest prompt"],
|
|
803
|
+
async run(ctx, inv) {
|
|
804
|
+
const key = inv.flags["no-key"] ? null : ingestKeyFor(ctx, str(inv.flags.key));
|
|
805
|
+
const text = buildAgentPrompt({
|
|
806
|
+
key,
|
|
807
|
+
repo: ctx.repo.config.repo ?? remoteUrl(ctx.cwd) ?? null,
|
|
808
|
+
platform: (str(inv.flags.platform) ?? "auto"),
|
|
809
|
+
endpoint: INGEST_URL,
|
|
810
|
+
docsUrl: INGEST_DOCS_URL,
|
|
811
|
+
});
|
|
812
|
+
if (ctx.flags.json) {
|
|
813
|
+
ctx.out.jsonOut({ ok: true, platform: str(inv.flags.platform) ?? "auto", has_key: Boolean(key), prompt: text });
|
|
814
|
+
return EXIT.OK;
|
|
815
|
+
}
|
|
816
|
+
ctx.out.log(text);
|
|
817
|
+
return EXIT.OK;
|
|
818
|
+
},
|
|
819
|
+
});
|
|
760
820
|
// ───────────────────────────── health ─────────────────────────────
|
|
761
821
|
export const healthCommand = cmd({
|
|
762
822
|
path: "health",
|
|
@@ -1034,4 +1094,4 @@ export const analyticsCommand = cmd({
|
|
|
1034
1094
|
});
|
|
1035
1095
|
},
|
|
1036
1096
|
});
|
|
1037
|
-
export const INGEST_COMMANDS = [connectCommand, issuesList, issuesShow, issuesResolve, issuesIgnore, issuesReopen, issuesTask, bugCommand, ingestKeys, ingestRevoke, ingestTest, ingestSnippetsCommand, ingestForward, ingestSettings, issuesUrgency, analyticsCommand, healthCommand];
|
|
1097
|
+
export const INGEST_COMMANDS = [connectCommand, issuesList, issuesShow, issuesDiagnose, issuesResolve, issuesIgnore, issuesReopen, issuesTask, bugCommand, ingestKeys, ingestRevoke, ingestTest, ingestSnippetsCommand, ingestPromptCommand, ingestForward, ingestSettings, issuesUrgency, analyticsCommand, healthCommand];
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prompt a developer hands to an AI agent (Claude Code, Cursor, Codex, …) to connect a repository to
|
|
3
|
+
* Scribe Clock: get an ingest key, store it, add a reporter for the app's stack, verify, commit.
|
|
4
|
+
*
|
|
5
|
+
* One source of truth, copied verbatim to apps/web/src/lib/ingestAgentPrompt.ts and
|
|
6
|
+
* scribeclock-cli/src/commands/ingestAgentPrompt.ts (no imports, so the same file runs in Deno, Vite and
|
|
7
|
+
* Node). It is shown on the Bug reports page (with the project's key), in the help guide (without a key),
|
|
8
|
+
* printed by `scribeclock ingest prompt`, and returned by the connect_repo MCP tool as `agent_prompt`.
|
|
9
|
+
*/
|
|
10
|
+
export const AGENT_PROMPT_PLATFORMS = [
|
|
11
|
+
{ id: "auto", label: "Detect from the code" },
|
|
12
|
+
{ id: "web", label: "Web (browser)" },
|
|
13
|
+
{ id: "ios", label: "iOS / macOS (Swift)" },
|
|
14
|
+
{ id: "android", label: "Android (Kotlin / Java)" },
|
|
15
|
+
{ id: "react-native", label: "React Native / Expo" },
|
|
16
|
+
{ id: "flutter", label: "Flutter" },
|
|
17
|
+
{ id: "node", label: "Node / backend (JS, TS)" },
|
|
18
|
+
{ id: "python", label: "Python" },
|
|
19
|
+
{ id: "other", label: "Other (any language)" },
|
|
20
|
+
];
|
|
21
|
+
const EVENT_SHAPE = `{
|
|
22
|
+
"type": "error | crash | bug_report | message | event",
|
|
23
|
+
"level": "fatal | error | warning | info",
|
|
24
|
+
"message": "Error message, bug report title, or analytics event name (for type event: $pageview, $click, or your own like checkout_started)",
|
|
25
|
+
"exception": { "type": "TypeError", "value": "x is undefined", "stacktrace": "text stack trace", "frames": [{ "file": "", "function": "", "line": 0, "col": 0, "in_app": true }] },
|
|
26
|
+
"description": "Bug report body (Markdown)",
|
|
27
|
+
"environment": "production | staging | development",
|
|
28
|
+
"release": "1.4.2 (app version + build)",
|
|
29
|
+
"platform": "web | ios | android | react-native | flutter | node | python | …",
|
|
30
|
+
"url": "https://…", "route": "/screen/or/route", "screen": "ScreenName", "element": "button:Save",
|
|
31
|
+
"session_id": "uuid per app session (new after 30 min idle)",
|
|
32
|
+
"user": { "id": "…", "email": "…", "name": "…" },
|
|
33
|
+
"tags": { "feature": "checkout" },
|
|
34
|
+
"context": { "device": { "model": "" }, "os": { "name": "", "version": "" }, "app": { "version": "", "build": "" }, "locale": "" },
|
|
35
|
+
"breadcrumbs": [{ "timestamp": "ISO-8601", "category": "ui.click | navigation | http | log", "message": "", "data": {} }],
|
|
36
|
+
"logs": [{ "timestamp": "ISO-8601", "level": "info", "message": "trace log line" }],
|
|
37
|
+
"attachments": [{ "url": "https://…/screenshot.png", "name": "screenshot.png", "type": "image/png" }],
|
|
38
|
+
"reporter": { "name": "", "email": "" },
|
|
39
|
+
"properties": { "any": "analytics event properties" },
|
|
40
|
+
"fingerprint": "optional grouping key", "event_id": "optional idempotency id", "timestamp": "ISO-8601"
|
|
41
|
+
}`;
|
|
42
|
+
function platformBlock(p, key, environment) {
|
|
43
|
+
switch (p) {
|
|
44
|
+
case "web":
|
|
45
|
+
return `WEB (browser): use the hosted SDK, it already does errors, sessions, page views and click autocapture.
|
|
46
|
+
- In the HTML document, before the app bundle:
|
|
47
|
+
<script src="https://scribeclock.com/sdk/scribeclock.js" data-key="${key}" data-environment="${environment ?? "production"}" data-release="<app version>" async></script>
|
|
48
|
+
(or import it and call ScribeClock.init({ key, environment, release }) in the app entry point; the key may be inlined — it is publishable and write-only).
|
|
49
|
+
- After sign-in: ScribeClock.setUser({ id, email, name }). On sign-out: ScribeClock.setUser(null).
|
|
50
|
+
- Call ScribeClock.track("event_name", { ...properties }) for the actions that matter (signup, checkout_started, export, …).
|
|
51
|
+
- Wire the app's feedback form to ScribeClock.reportBug({ title, description, email, screenshotUrl }).
|
|
52
|
+
- Mark private form areas with data-sc-private so autocapture never records them.
|
|
53
|
+
- SPA: the SDK sends $pageview on history changes; for a custom router call ScribeClock.pageview(screenName).`;
|
|
54
|
+
case "ios":
|
|
55
|
+
return `iOS / macOS (Swift): write ONE dependency-free file, Sources/ScribeClock/ScribeClock.swift (or in the app target), using URLSession:
|
|
56
|
+
- ScribeClock.configure(key:environment:release:) — read the key from Info.plist (SCRIBECLOCK_INGEST_KEY filled from an .xcconfig / CI secret) or from the environment in Debug. release = CFBundleShortVersionString + " (" + CFBundleVersion + ")".
|
|
57
|
+
- Crashes: install NSSetUncaughtExceptionHandler and signal handlers (SIGABRT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP) that write a small JSON crash file (type "crash", exception type/value, Thread.callStackSymbols, breadcrumbs, last logs, context) to Application Support using only async-signal-safe work where possible; on the next launch read it, POST it, delete it.
|
|
58
|
+
- Errors: capture(_ error: Error, level:), captureMessage(_:level:). Never throw from the reporter.
|
|
59
|
+
- Breadcrumbs (ring buffer of 100): screen changes, taps, HTTP requests (method, path, status), app lifecycle. Logs (ring buffer of 300 lines) via log(_:level:).
|
|
60
|
+
- Analytics: a session id (UUID) created at launch and renewed after 30 minutes in the background; screen(_ name: String) sends {"type":"event","name":"$pageview","screen":name}; tap(_ element: String, screen:) sends {"name":"$click","element":element}; track(_ name:, properties:) for custom events; setUser(id:email:name:). Provide a SwiftUI View modifier .scribeClockScreen("Name") (onAppear) and, for UIKit, an optional viewDidAppear swizzle.
|
|
61
|
+
- Bug reports: reportBug(title:description:email:screenshot:) — upload the screenshot wherever the app already stores files and pass its URL in attachments, or omit it.
|
|
62
|
+
- Transport: queue events in memory and on disk, batch up to 50, flush every 10 s and when the app enters the background (use a background URLSession configuration so the request completes), retry with exponential backoff, drop after 3 attempts. Headers: Authorization: Bearer <key>, Content-Type: application/json. Every event carries environment, release, platform "ios" (or "macos"), context { device.model, os.name/version, app.version/build, locale }, session_id and user.
|
|
63
|
+
- Add XCTest coverage for batching, the 30-minute session rule, and the crash file round trip.`;
|
|
64
|
+
case "android":
|
|
65
|
+
return `ANDROID (Kotlin): write a small ScribeClock object (OkHttp or HttpURLConnection, no other dependencies):
|
|
66
|
+
- configure(context, key, environment, release = BuildConfig.VERSION_NAME + " (" + VERSION_CODE + ")"); the key comes from BuildConfig / local.properties / CI secret, never from source control.
|
|
67
|
+
- Crashes: Thread.setDefaultUncaughtExceptionHandler → write a crash JSON file (type "crash", exception class, message, stack trace, breadcrumbs, logs, context) then chain to the previous handler; on next start POST and delete it.
|
|
68
|
+
- captureException(Throwable, level), captureMessage, breadcrumb(category, message, data) ring buffer of 100, log(level, message) ring buffer of 300.
|
|
69
|
+
- Analytics: session id renewed after 30 min in background (ProcessLifecycleOwner), screen(name) → $pageview (hook Activity/Fragment lifecycle or Navigation's addOnDestinationChangedListener), click(element, screen) → $click, track(name, properties), setUser(id, email, name).
|
|
70
|
+
- reportBug(title, description, email, screenshotUrl) for the in-app feedback screen.
|
|
71
|
+
- Transport: in-memory + file queue, batches of 50, flush every 10 s and onStop/WorkManager for background delivery, exponential backoff, drop after 3 attempts. Headers: Authorization: Bearer <key>. Every event carries environment, release, platform "android", context { device.model, os.version, app.version/build, locale }, session_id, user.`;
|
|
72
|
+
case "react-native":
|
|
73
|
+
return `REACT NATIVE / EXPO (TypeScript): write src/scribeclock.ts (fetch only):
|
|
74
|
+
- init({ key, environment, release: Application.nativeApplicationVersion + build }); key from app config / EAS secret (EXPO_PUBLIC_SCRIBECLOCK_INGEST_KEY) — publishable, write-only.
|
|
75
|
+
- Crashes/errors: ErrorUtils.setGlobalHandler((error, isFatal) => send({ type: isFatal ? "crash" : "error", exception: { type: error.name, value: error.message, stacktrace: error.stack } })) and chain the previous handler; also hook unhandled promise rejections.
|
|
76
|
+
- Breadcrumbs (100) from navigation state changes, presses and fetch; logs (300) from console.* in development builds only.
|
|
77
|
+
- Analytics: session id renewed after 30 min via AppState; screen(name) → $pageview on React Navigation's onStateChange; press(element, screen) → $click; track(name, props); setUser(user).
|
|
78
|
+
- reportBug({ title, description, email, screenshotUrl }).
|
|
79
|
+
- Transport: queue, batches of 50, flush every 10 s and on AppState "background", retry with backoff, drop after 3 attempts. platform = Platform.OS; context from expo-device / react-native-device-info if present.`;
|
|
80
|
+
case "flutter":
|
|
81
|
+
return `FLUTTER (Dart): write lib/scribeclock.dart (package:http only):
|
|
82
|
+
- ScribeClock.configure(key:, environment:, release:) from --dart-define=SCRIBECLOCK_INGEST_KEY=…; release from package_info_plus.
|
|
83
|
+
- Crashes: FlutterError.onError and PlatformDispatcher.instance.onError → type "crash"/"error" with exception type, toString(), stack trace; chain the previous handlers.
|
|
84
|
+
- captureException, captureMessage, breadcrumb (100), log (300), screen(name) via a NavigatorObserver → $pageview, tap(element, screen) → $click, track(name, properties), setUser, reportBug(title, description, email, screenshotUrl).
|
|
85
|
+
- Session id renewed after 30 min in background (WidgetsBindingObserver lifecycle); batches of 50, flush every 10 s and on paused, backoff, drop after 3 attempts; context from device_info_plus.`;
|
|
86
|
+
case "node":
|
|
87
|
+
return `NODE / BACKEND (JavaScript or TypeScript): write src/scribeclock.ts (global fetch only):
|
|
88
|
+
- init({ key: process.env.SCRIBECLOCK_INGEST_KEY, environment: process.env.NODE_ENV, release: package.json version or the git SHA, platform: "node" }).
|
|
89
|
+
- process.on("uncaughtException") and process.on("unhandledRejection") → send type "crash" (then exit as before); an Express/Fastify/Koa error middleware → type "error" with { url, route, user, request id }; a logger transport (pino/winston) that keeps the last 300 lines as logs and attaches them to the next error.
|
|
90
|
+
- captureException(err, { route, user, tags }), captureMessage, track(name, properties, { user }) for product analytics from the backend (signup_completed, order_paid …).
|
|
91
|
+
- Transport: batch up to 50, flush every 5 s and on SIGTERM/beforeExit (await the last flush), retry with backoff, drop after 3 attempts, never throw.`;
|
|
92
|
+
case "python":
|
|
93
|
+
return `PYTHON: write scribeclock.py (requests or urllib only):
|
|
94
|
+
- init(key=os.environ["SCRIBECLOCK_INGEST_KEY"], environment=os.environ.get("APP_ENV"), release=<version or git SHA>, platform="python").
|
|
95
|
+
- sys.excepthook (and threading.excepthook) → type "crash" with exception type, str(exc), traceback.format_exc(); a logging.Handler that keeps the last 300 records and attaches them; framework hooks: Django middleware / Flask errorhandler / FastAPI exception handler → type "error" with url, route, user.
|
|
96
|
+
- capture_exception(exc, route=None, user=None, tags=None), capture_message, track(name, properties, user=None).
|
|
97
|
+
- Transport: a background thread with a queue, batches of 50, flush every 5 s and at exit (atexit), retry with backoff, drop after 3 attempts, never raise into the app.`;
|
|
98
|
+
default:
|
|
99
|
+
return `ANY LANGUAGE: write a small reporter module (HTTP client only) with: configure(key, environment, release); capture_exception / capture_message; a global unhandled-exception / crash hook that sends type "crash"; breadcrumbs (last 100) and logs (last 300) attached to each report; analytics: session id (new after 30 min idle), screen → {"type":"event","name":"$pageview","screen":…}, click → {"name":"$click","element":…}, track(name, properties), set_user; report_bug(title, description, email, screenshot_url); batched transport (≤ 50 events, flush every 10 s and on exit / background, backoff, drop after 3 attempts, never throw into the app).`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Markdown prompt for an agent; `key` present → it is embedded (the key is publishable and write-only). */
|
|
103
|
+
export function buildAgentPrompt(input = {}) {
|
|
104
|
+
const endpoint = input.endpoint ?? "https://scribeclock.com/ingest";
|
|
105
|
+
const docs = input.docsUrl ?? "https://scribeclock.com/help/bug-reports";
|
|
106
|
+
const platform = (AGENT_PROMPT_PLATFORMS.some((p) => p.id === input.platform) ? input.platform : "auto");
|
|
107
|
+
const key = input.key?.trim() || "";
|
|
108
|
+
const keyRef = key || "$SCRIBECLOCK_INGEST_KEY";
|
|
109
|
+
const env = input.environment?.trim() || null;
|
|
110
|
+
const project = input.projectName?.trim() ? `Project: ${input.projectName.trim()}${input.repo ? ` (${input.repo})` : ""}` : input.repo ? `Repository: ${input.repo}` : null;
|
|
111
|
+
const getKey = key
|
|
112
|
+
? `1. The ingest key is above. Store it as SCRIBECLOCK_INGEST_KEY in the app's configuration (environment variable, secrets manager, .xcconfig / BuildConfig / --dart-define / app config). It is publishable and write-only, but keep it out of git anyway (add any file that holds it to .gitignore).`
|
|
113
|
+
: `1. Get the project's ingest key (sc_ing_…), in this order:
|
|
114
|
+
a. If you have the Scribe Clock MCP server: call connect_repo with repo = "owner/name" from \`git remote get-url origin\` (add create_project=true if no project lists this repository). The reply contains SCRIBECLOCK_INGEST_KEY and an agent_prompt with stack-specific steps.
|
|
115
|
+
b. Else if Node is available: run \`npx scribeclock connect --env\` inside the repository (sign in when it asks); it writes .scribeclock.json and puts SCRIBECLOCK_INGEST_KEY into .env.
|
|
116
|
+
c. Else ask the developer to copy the key from the project's Bug reports page in Scribe Clock (sidebar → App analytics → Bug reports).
|
|
117
|
+
Store it as SCRIBECLOCK_INGEST_KEY in the app's configuration (environment variable, secrets manager, .xcconfig / BuildConfig / --dart-define / app config). It is publishable and write-only, but keep it out of git anyway.`;
|
|
118
|
+
const stack = platform === "auto"
|
|
119
|
+
? `3. Detect the stack from the repository (package.json, Package.swift / *.xcodeproj, build.gradle, pubspec.yaml, pyproject / requirements) and follow the matching block below. A monorepo gets one reporter per app, all with the same key.
|
|
120
|
+
|
|
121
|
+
${["web", "ios", "android", "react-native", "flutter", "node", "python"].map((p) => ` ${platformBlock(p, keyRef, env).replace(/\n/g, "\n ")}`).join("\n\n")}`
|
|
122
|
+
: `3. ${platformBlock(platform, keyRef, env)}`;
|
|
123
|
+
return [
|
|
124
|
+
`Connect this repository to Scribe Clock (${docs.replace(/\/help.*$/, "")}) so that errors, crashes, user bug reports, trace logs and product analytics from the app flow into Scribe Clock. There, every new problem becomes a task for the team (bundled with its duplicates, carrying an urgency), and the project's Analytics page shows active users, sessions, retention, screens, clicks, releases and crash-free rates.`,
|
|
125
|
+
``,
|
|
126
|
+
...(project ? [project] : []),
|
|
127
|
+
`Ingest endpoint: ${endpoint} (POST JSON, header \`Authorization: Bearer <key>\`; one event, or {"events":[…]} with up to 50)`,
|
|
128
|
+
...(key ? [`Ingest key: ${key}`] : []),
|
|
129
|
+
`Docs (read first): ${docs}`,
|
|
130
|
+
...(env ? [`Default environment: ${env}`] : []),
|
|
131
|
+
``,
|
|
132
|
+
`Do this, in order, and do not stop until step 5 passes:`,
|
|
133
|
+
``,
|
|
134
|
+
getKey,
|
|
135
|
+
``,
|
|
136
|
+
`2. Install nothing heavy: the reporter is one small file using the platform's own HTTP client. Do not add Sentry, Firebase or other SDKs for this.`,
|
|
137
|
+
``,
|
|
138
|
+
stack,
|
|
139
|
+
``,
|
|
140
|
+
`4. Whatever the stack, the reporter must:
|
|
141
|
+
- send uncaught exceptions / crashes (type "crash" when the app dies, "error" otherwise) with exception type, message and stack trace;
|
|
142
|
+
- offer captureException / captureMessage / reportBug(title, description, email, screenshot) for the app to call;
|
|
143
|
+
- attach the last 100 breadcrumbs (navigation, taps/clicks, HTTP calls, lifecycle) and the last 300 log lines to every report;
|
|
144
|
+
- send analytics: a session_id that renews after 30 minutes idle, setUser({ id, email, name }), a $pageview event with "screen" on every screen change, a $click event with "element" on taps of buttons/links, and track(name, properties) for the actions that matter;
|
|
145
|
+
- put environment, release (version + build), platform and context { device, os, app, locale } on every event;
|
|
146
|
+
- batch (≤ 50), flush periodically and on background/exit, retry with backoff, drop after 3 attempts, never block or crash the app, and strip passwords, tokens and card numbers from anything it sends.`,
|
|
147
|
+
``,
|
|
148
|
+
`5. Verify end to end: send one event {"type":"message","level":"info","message":"scribeclock connect test"} with the key and confirm the response is HTTP 200 with "ok": true; then send one type "error" with a fake stack trace and confirm it shows up — with the MCP server call project_health (or list_issues), with the CLI run \`scribeclock health\`, otherwise ask the developer to check the project's Bug reports page. Resolve that test issue afterwards (update_issue status=resolved / \`scribeclock issues resolve <id>\`).`,
|
|
149
|
+
``,
|
|
150
|
+
`6. Commit the reporter and a short README section ("Crash reports and analytics go to Scribe Clock; set SCRIBECLOCK_INGEST_KEY"), without the key. Report back with: where the key is read from, what is captured automatically, and the helpers the app can call.`,
|
|
151
|
+
``,
|
|
152
|
+
`Event shape (every field optional except type and message / exception):`,
|
|
153
|
+
EVENT_SHAPE,
|
|
154
|
+
].join("\n");
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scribeclock",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "ScribeClock from the terminal: tasks, time clock, projects, PRDs, meetings, files, vault and Cloudflare — a thin client over the ScribeClock MCP server.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Haydar Al-Karar LLC, dba Karar Tech",
|