Skip to content

Channel CONTEXT.md: agent generation + inject into task prompts (local & cloud)#2627

Draft
raquelmsmith wants to merge 4 commits into
mainfrom
posthog-code/context-md-agent
Draft

Channel CONTEXT.md: agent generation + inject into task prompts (local & cloud)#2627
raquelmsmith wants to merge 4 commits into
mainfrom
posthog-code/context-md-agent

Conversation

@raquelmsmith

Copy link
Copy Markdown
Member

What

Builds out the per-channel CONTEXT.md (folder instructions) feature across three areas, for both local and cloud runs:

1. Generate CONTEXT.md with an agent

  • The CONTEXT.md empty state gains a "Generate with agent" action with a Local/Cloud toggle (reusing FolderPicker / GitHubRepoPicker + useUserRepositoryIntegration).
  • Generation runs as a normal task in the channel's repo that explores the code + PostHog data (flags, experiments, surveys, notebooks, insights, analytics) and publishes the file via the PostHog MCP (desktop-file-system-instructions-partial-update).
  • The view tracks that task: a centered "Generating" + View task state while it runs, "Generate again" if it stops before publishing, and the rendered doc once published. Status is environment-aware (cloud cloudStatus/latest_run.status, local live session).

2. Channel CONTEXT.md injected into task prompts

  • When a task is created from a channel, that channel's CONTEXT.md is appended to the agent's initial prompt as optional background — framed as reference material, not instructions. Works for local and cloud tasks.
  • New-task input shows a dismissable Using: #channel CONTEXT.md chip (dismiss drops it from the prompt).

3. Conversation rendering

  • The injected <channel_context> block renders as a clickable #channel CONTEXT.md tag instead of inline text; clicking opens the exact snapshot that was sent as a real file tab in the split (a new context panel-tab type), not a live re-fetch.

Shared, server-side generation tracking

  • "Which task is generating this folder's CONTEXT.md" is stored server-side, keyed on the folder (project-shared, visible to all users), replacing fragile local electronStorage. New api-client get/setDesktopFolderGenerationTask + useFolderGenerationTask hook.

⚠️ Requires a backend change (separate PostHog cloud repo)

The shared generation indicator depends on new endpoints that must be added in the PostHog cloud repo:

GET /api/projects/{team_id}/desktop_file_system/{id}/context_generation/  -> { task_id: uuid|null }
PUT /api/projects/{team_id}/desktop_file_system/{id}/context_generation/  body { task_id: uuid|null }

plus auto-clear of the association when a new folder-instructions version is published.

Until that lands, the client no-ops gracefully: generation still runs and the file still renders on publish — only the project-shared in-progress indicator is dormant. (Everything else in this PR works without backend changes.)

Testing

  • Full pnpm typecheck (22/22), pnpm lint, node scripts/check-host-boundaries.mjs — all green.
  • Unit tests: prompt-builder.test.ts (channel-context block/text), channelContext.test.ts (conversation tag parser).
  • Manual: local + cloud generate, channel-context injection + tag for both run modes.

Created with PostHog Code

Adds a one-shot background agent to the per-channel CONTEXT.md empty state.
The agent explores a selected local repo (read-only) plus PostHog data via the
PostHog MCP for things related to the folder name, then publishes CONTEXT.md
itself through the MCP (desktop-file-system-instructions-partial-update).

- New ContextGenService (host-router), mirroring CanvasGenService: real repoPath,
  denies file-write/shell/network tools, keeps MCP enabled, streams prose + tool
  activity. Bound as a singleton with a BindingMap entry.
- Core schemas + identifier + service interface; one-line tRPC router registered
  in both host-router and apps/code aggregators.
- UI: folder picker (existing registered repos), context-gen store + stream
  subscription, GeneratingPanel reusing MarkdownRenderer, done -> refetch + render.

v1 is local-repo only; cloud/GitHub-sandbox mode is a follow-up.

Generated-By: PostHog Code
Task-Id: 8b4fb28f-6761-49c4-9b2f-dc62f2305ca2
When a task is created from a channel, that channel's CONTEXT.md is appended
to the agent's initial prompt as optional background — framed as reference
material, not instructions, so the agent can use it as a starting point
without being limited to it.

Threads an optional channelContext string from WebsiteNewTask (fetched via
useFolderInstructions) through TaskInput -> useTaskCreation -> prepareTaskInput
-> TaskCreationInput, and appends it as a labeled <channel_context> block to
the initial prompt in TaskCreationSaga. Empty/missing instructions inject
nothing.

Generated-By: PostHog Code
Task-Id: 8b4fb28f-6761-49c4-9b2f-dc62f2305ca2
Three related changes to how a channel's CONTEXT.md flows through tasks:

- New-task input: when a channel has a CONTEXT.md, show a dismissable
  "Using: #channel CONTEXT.md" chip; dismissing drops it from the prompt.
- Conversation: the injected <channel_context> block renders as a clickable
  "#channel CONTEXT.md" tag instead of inline text; clicking opens the exact
  snapshot that was sent as a real file tab in the split (not a fetched/live
  copy), via a new "context" panel tab type.
- Generation: replace the bespoke streaming generator with a normal task in
  the channel's repo that publishes CONTEXT.md via the PostHog MCP. The view
  tracks that task — a centered "Generating" state with a View task button
  while its session runs, and a re-generate path if it stops before producing
  a file. Removes ContextGenService, its router/schemas/store/subscriptions,
  and the CONTEXT_GEN_SERVICE DI wiring.

Generated-By: PostHog Code
Task-Id: 8b4fb28f-6761-49c4-9b2f-dc62f2305ca2
…n tracking

- Channel-context injection now reaches cloud tasks: split buildChannelContextText
  out of buildChannelContextBlock and fold it into the cloud task's
  pendingUserMessage (local still appends a block to initialPrompt). The shared
  conversation tag parser renders both identically; task titles stay clean.
- CONTEXT.md generation works for cloud: the generate picker has a Local/Cloud
  toggle reusing FolderPicker / GitHubRepoPicker + useUserRepositoryIntegration;
  useGenerateContext builds a local or cloud TaskCreationInput. The "generating"
  status is now environment-aware (cloud uses cloudStatus/latest_run.status,
  local uses the live session).
- Generation tracking moved off fragile local electronStorage to a server-side,
  project-shared association keyed on the folder (works before any instructions
  exist): new api-client get/setDesktopFolderGenerationTask + useFolderGenerationTask
  hook. Deleted contextGenTaskStore.

Requires a PostHog cloud backend change (separate repo) to add the
GET/PUT .../desktop_file_system/{id}/context_generation/ endpoints; until then the
client no-ops gracefully (generation runs and the file renders on publish, only
the shared in-progress indicator is dormant).

Generated-By: PostHog Code
Task-Id: 8b4fb28f-6761-49c4-9b2f-dc62f2305ca2
@github-actions

Copy link
Copy Markdown

React Doctor could not complete this scan.

No React dependency found in /tmp/react-doctor-baseline-14U8ZC/package.json. Add "react" to dependencies (or peerDependencies) and re-run.

Reviewed by React Doctor for commit 2f60498.

@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/core/src/editor/prompt-builder.test.ts:7-11
The null/empty cases for both `buildChannelContextText` and `buildChannelContextBlock` are tested with multiple assertions inside a single `it` block. The team rule is to always prefer parameterised tests (`it.each`), which would also give a clear label per failing input if one regresses.

```suggestion
describe("buildChannelContextText", () => {
  it.each([
    [undefined],
    ["   \n "],
  ] as const)("returns null for empty or whitespace content (%s)", (input) => {
    expect(buildChannelContextText(input)).toBeNull();
  });
```

### Issue 2 of 2
packages/core/src/editor/prompt-builder.test.ts:29-35
Same pattern — four inputs collapsed into one `it` block. Each would be a separate row in an `it.each` table, making it immediately obvious which specific input fails.

```suggestion
describe("buildChannelContextBlock", () => {
  it.each([
    [undefined],
    [null],
    [""],
    ["   \n  "],
  ] as const)("returns null for empty or whitespace content (%s)", (input) => {
    expect(buildChannelContextBlock(input)).toBeNull();
  });
```

Reviews (1): Last reviewed commit: "feat(canvas): cloud support for channel ..." | Re-trigger Greptile

Comment on lines +7 to +11
describe("buildChannelContextText", () => {
it("returns null for empty or whitespace content", () => {
expect(buildChannelContextText(undefined)).toBeNull();
expect(buildChannelContextText(" \n ")).toBeNull();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The null/empty cases for both buildChannelContextText and buildChannelContextBlock are tested with multiple assertions inside a single it block. The team rule is to always prefer parameterised tests (it.each), which would also give a clear label per failing input if one regresses.

Suggested change
describe("buildChannelContextText", () => {
it("returns null for empty or whitespace content", () => {
expect(buildChannelContextText(undefined)).toBeNull();
expect(buildChannelContextText(" \n ")).toBeNull();
});
describe("buildChannelContextText", () => {
it.each([
[undefined],
[" \n "],
] as const)("returns null for empty or whitespace content (%s)", (input) => {
expect(buildChannelContextText(input)).toBeNull();
});

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/editor/prompt-builder.test.ts
Line: 7-11

Comment:
The null/empty cases for both `buildChannelContextText` and `buildChannelContextBlock` are tested with multiple assertions inside a single `it` block. The team rule is to always prefer parameterised tests (`it.each`), which would also give a clear label per failing input if one regresses.

```suggestion
describe("buildChannelContextText", () => {
  it.each([
    [undefined],
    ["   \n "],
  ] as const)("returns null for empty or whitespace content (%s)", (input) => {
    expect(buildChannelContextText(input)).toBeNull();
  });
```

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +29 to +35
describe("buildChannelContextBlock", () => {
it("returns null for empty or whitespace content", () => {
expect(buildChannelContextBlock(undefined)).toBeNull();
expect(buildChannelContextBlock(null)).toBeNull();
expect(buildChannelContextBlock("")).toBeNull();
expect(buildChannelContextBlock(" \n ")).toBeNull();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Same pattern — four inputs collapsed into one it block. Each would be a separate row in an it.each table, making it immediately obvious which specific input fails.

Suggested change
describe("buildChannelContextBlock", () => {
it("returns null for empty or whitespace content", () => {
expect(buildChannelContextBlock(undefined)).toBeNull();
expect(buildChannelContextBlock(null)).toBeNull();
expect(buildChannelContextBlock("")).toBeNull();
expect(buildChannelContextBlock(" \n ")).toBeNull();
});
describe("buildChannelContextBlock", () => {
it.each([
[undefined],
[null],
[""],
[" \n "],
] as const)("returns null for empty or whitespace content (%s)", (input) => {
expect(buildChannelContextBlock(input)).toBeNull();
});

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/editor/prompt-builder.test.ts
Line: 29-35

Comment:
Same pattern — four inputs collapsed into one `it` block. Each would be a separate row in an `it.each` table, making it immediately obvious which specific input fails.

```suggestion
describe("buildChannelContextBlock", () => {
  it.each([
    [undefined],
    [null],
    [""],
    ["   \n  "],
  ] as const)("returns null for empty or whitespace content (%s)", (input) => {
    expect(buildChannelContextBlock(input)).toBeNull();
  });
```

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant