Skip to content

fix(agents-cli) Inkeep pull uses human-readable stems for file imports#2468

Open
miles-kt-inkeep wants to merge 6 commits intomainfrom
fix/pull-import-file-names
Open

fix(agents-cli) Inkeep pull uses human-readable stems for file imports#2468
miles-kt-inkeep wants to merge 6 commits intomainfrom
fix/pull-import-file-names

Conversation

@miles-kt-inkeep
Copy link
Contributor

No description provided.

@vercel
Copy link

vercel bot commented Mar 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Mar 3, 2026 2:11pm
agents-docs Ready Ready Preview, Comment Mar 3, 2026 2:11pm
agents-manage-ui Ready Ready Preview, Comment Mar 3, 2026 2:11pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Mar 2, 2026

🦋 Changeset detected

Latest commit: 92f36e7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@inkeep/agents-cli Patch
@inkeep/agents-api Patch
@inkeep/agents-manage-ui Patch
@inkeep/agents-core Patch
@inkeep/agents-email Patch
@inkeep/agents-mcp Patch
@inkeep/agents-sdk Patch
@inkeep/agents-work-apps Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pullfrog
Copy link
Contributor

pullfrog bot commented Mar 2, 2026

Summary: Makes inkeep pull generate human-readable file names (e.g. my-agent-abc123.ts instead of raw UUIDs) and updates all cross-file import paths to match.

  • agents-cli/src/commands/pull-v4/utils.ts — adds buildComponentFileName usage, plus a module-level _importStems registry with setImportStems/getImportStem helpers so generators can resolve the new file stems at import time
  • agents-cli/src/commands/pull-v4/component-registry.ts — every registry.register call now derives file paths via buildComponentFileName(id, name) instead of using the raw ID
  • agents-cli/src/commands/pull-v4/introspect-generator.ts — splits generation into a collect-then-generate two-pass loop: first pass builds the importStems map from all records, second pass generates source files (ensures stems are available before any generator emits import statements)
  • agents-cli/src/commands/pull-v4/generators/agent-generator.ts — switches all moduleSpecifier paths (context-configs, sub-agents, triggers, scheduled-triggers, status-components) to use getImportStem
  • agents-cli/src/commands/pull-v4/generators/sub-agent-generator.ts — same treatment for context-config, tool, delegate, and reference imports
  • agents-cli/src/commands/pull-v4/generators/context-config-generator.ts — credential import paths use getImportStem
  • agents-cli/src/commands/pull-v4/generators/external-agent-generator.ts, project-generator.ts, trigger-generator.ts — credential and reference import paths updated
  • .changeset/royal-violet-kiwi.md — patch changeset for @inkeep/agents-cli

Pullfrog  | View workflow run | Using Claude Code | Triggered by Pullfrogpullfrog.com𝕏

Copy link
Contributor

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

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

The approach is clean — two-pass collect-then-generate so all file stems are known before any generator emits import paths. Two issues to address:

Bug: context config fallback paths not updated. collectContextConfigRecords (line 360) and collectAgentContextTemplateReferences (line 995) in introspect-generator.ts still use `${contextConfigId}.ts` as the fallback, while every other collector uses buildComponentFileName. On a fresh pull with opaque IDs, this produces e.g. cm82v49jh001upc01lo2ddxn2.ts instead of my-context-config-lo2ddxn2.ts, and the import stem in _importStems will be the raw UUID — mismatching the file name other generators expect.

registerAllComponents is dead code. It is exported from component-registry.ts but never imported or called anywhere. The changes to it in this PR have no runtime effect. The actual file path assignments come from the individual collect*Records functions in introspect-generator.ts. Not blocking, but worth knowing — the function could be removed or wired up in a follow-up.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow runpullfrog.com𝕏

Comment on lines +158 to +166
let _importStems: Record<string, string> = {};

export function setImportStems(stems: Record<string, string>): void {
_importStems = stems;
}

export function getImportStem(id: string): string {
return _importStems[id] ?? id;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Module-level mutable state with no reset. If introspectGenerate is called more than once in the same process (tests, watch mode, or a future batch workflow), stale stems from a previous run persist and silently win via the ??= in the collection loop. Consider calling _importStems = {} at the top of introspectGenerate (or adding a clearImportStems() helper) so each invocation starts clean.

Comment on lines +149 to +150
collectedRecords.push({ generate: task.generate, record });
importStems[record.id] ??= basename(record.filePath, '.ts');
Copy link
Contributor

Choose a reason for hiding this comment

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

The stem for context configs will be wrong on a fresh pull. collectContextConfigRecords (line 360) still uses `${contextConfigId}.ts` as the fallback file path, so basename(record.filePath, '.ts') here produces the raw UUID. Every other collector passes the name through buildComponentFileName. The same issue exists at line 995 (collectAgentContextTemplateReferences).

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(3) Total Issues | Risk: Medium

🟠⚠️ Major (2) 🟠⚠️

Inline Comments:

  • 🟠 Major: utils.ts:165 Silent fallback in getImportStem() produces broken imports when component ID is missing
  • 🟠 Major: utils.ts:160-166 New setImportStems()/getImportStem() functions lack unit test coverage

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: utils.ts:158-166 Module-level state lacks clearImportStems() reset function for test isolation

🕐 Pending Recommendations (3)


🚫 REQUEST CHANGES

Summary: The two-pass collect-then-generate architecture is clean and correct for solving the human-readable file naming problem. However, there are two critical gaps that need addressing before this PR can ship reliably:

  1. Context config fallback paths at lines 360 and 995 still use raw IDs instead of buildComponentFileName(). This means context configs with opaque CUIDs will get incorrectly named files, and the import stems will mismatch. This was already identified in the prior review and remains unresolved.

  2. The silent fallback in getImportStem() masks bugs by producing broken imports that only fail at TypeScript compile time. Consider failing fast with a descriptive error to surface registration issues during inkeep pull rather than deferring failures.

The test coverage and reset function suggestions are important for maintainability but are less urgent than fixing the context config paths.

Discarded (4)
Location Issue Reason Discarded
utils.ts:158 Naming convention inconsistency — underscore prefix _importStems Mixed precedent in codebase; 50/50 developer preference
introspect-generator.ts:150 ??= operator silently ignores duplicate IDs Intentional first-wins semantics; edge case unlikely in practice
component-registry.ts Dead code changes Already raised in prior review — routed to Pending
Generator tests Tests don't use opaque IDs Useful improvement but not blocking; existing integration tests exercise the paths
Reviewers (5)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-errors 3 0 0 0 2 0 1
pr-review-tests 4 0 0 0 1 2 1
pr-review-consistency 2 0 0 0 0 0 2
pr-review-precision 3 0 0 0 0 3 0
pr-review-standards 0 0 0 0 0 0 0
Total 12 0 0 0 3 3 4

Note: Standards reviewer found no issues beyond what was already raised in prior review.

}

export function getImportStem(id: string): string {
return _importStems[id] ?? id;
Copy link
Contributor

Choose a reason for hiding this comment

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

🟠 MAJOR: Silent fallback produces broken imports

Issue: getImportStem(id) silently falls back to the raw ID when a component is not found in _importStems. This produces syntactically valid but semantically broken import paths that fail at TypeScript compile time with "Cannot find module" errors.

Why: If a component ID isn't registered (due to a missing collector, typo, or unsupported component type), generated imports will reference non-existent files like ../credentials/cm82v49jh001upc01lo2ddxn2 instead of ../credentials/api-key-lo2ddxn2. This failure only surfaces after full code generation, making debugging difficult since the root cause (missing stem) isn't obvious from the TypeScript error.

Fix: Consider failing fast with a descriptive error:

export function getImportStem(id: string): string {
  const stem = _importStems[id];
  if (!stem) {
    throw new Error(
      `Import stem not found for component ID '${id}'. ` +
      `Ensure the component is registered via collectXxxRecords() before code generation.`
    );
  }
  return stem;
}

Alternatively, if graceful degradation is acceptable, emit a warning:

if (!stem) {
  console.warn(`Warning: Import stem not found for '${id}'. Using raw ID as fallback.`);
  return id;
}

Refs:

Comment on lines +158 to +166
let _importStems: Record<string, string> = {};

export function setImportStems(stems: Record<string, string>): void {
_importStems = stems;
}

export function getImportStem(id: string): string {
return _importStems[id] ?? id;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Minor: Missing reset function for test isolation

Issue: The _importStems module-level state has a setter but no reset function. While setImportStems() replaces the entire object (avoiding stale entry accumulation), there's no explicit clearImportStems() for test teardown.

Why: The codebase convention for module-level state (e.g., resetDevConfigCache, resetSpiceClient, resetEnv, clearJwksCache) includes reset/clear functions to ensure test isolation. Generator unit tests that call getImportStem() currently rely on the fallback behavior without verifying stem resolution works correctly with populated data.

Fix: Consider adding a reset helper:

Suggested change
let _importStems: Record<string, string> = {};
export function setImportStems(stems: Record<string, string>): void {
_importStems = stems;
}
export function getImportStem(id: string): string {
return _importStems[id] ?? id;
}
let _importStems: Record<string, string> = {};
export function setImportStems(stems: Record<string, string>): void {
_importStems = stems;
}
export function clearImportStems(): void {
_importStems = {};
}
export function getImportStem(id: string): string {
return _importStems[id] ?? id;
}

Refs:

Comment on lines +160 to +166
export function setImportStems(stems: Record<string, string>): void {
_importStems = stems;
}

export function getImportStem(id: string): string {
return _importStems[id] ?? id;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🟠 MAJOR: New functions lack unit test coverage

Issue: The setImportStems() / getImportStem() functions implement the core import path resolution logic but have no dedicated unit tests. These functions are called by every generator but are only exercised indirectly through integration tests that use human-readable fixture IDs.

Why: Without tests, the following bugs could be introduced undetected: (1) If getImportStem is called before setImportStems, all imports use raw IDs, (2) The fallback behavior (_importStems[id] ?? id) is untested, (3) There's no verification that module-level state is correctly initialized.

Fix: Add unit tests in utils.test.ts:

describe('getImportStem', () => {
  afterEach(() => {
    setImportStems({}); // Reset state
  });

  it('returns id when no stems are set', () => {
    expect(getImportStem('cm82v49jh001upc01lo2ddxn2')).toBe('cm82v49jh001upc01lo2ddxn2');
  });

  it('returns mapped stem when set', () => {
    setImportStems({ 'cm82v49jh001upc01lo2ddxn2': 'support-agent-lo2ddxn2' });
    expect(getImportStem('cm82v49jh001upc01lo2ddxn2')).toBe('support-agent-lo2ddxn2');
  });

  it('returns id for unmapped keys', () => {
    setImportStems({ 'other-id': 'other-stem' });
    expect(getImportStem('missing-id')).toBe('missing-id');
  });
});

Refs:

@github-actions github-actions bot deleted a comment from claude bot Mar 2, 2026
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) New Issues | (3) Pending | Risk: Medium

Delta Review: c468a6cdfsetImportStems({}) reset

The single-line change at introspect-generator.ts:136 correctly addresses the prior feedback about module-level state persistence. By calling setImportStems({}) at the start of introspectGenerate(), stale stems from previous runs are cleared — preventing silent bugs in watch mode, tests, or batch workflows.

No new issues introduced by this delta.

🕐 Pending Recommendations (3)

These issues were raised in prior reviews and remain unresolved:

  • 🔴 introspect-generator.ts:361 Context config fallback path uses ${contextConfigId}.ts instead of buildComponentFileName()
  • 🔴 introspect-generator.ts:996 Same issue in collectAgentContextTemplateReferences() fallback path
  • 🟠 utils.ts:165 Silent fallback in getImportStem() produces broken imports when component ID is missing

Notes

  • The clearImportStems() export suggested in prior feedback was not added, but the current approach (setImportStems({})) achieves the same effect and is already in use. Acceptable.
  • The test coverage gap for setImportStems()/getImportStem() remains but is not blocking.
  • registerAllComponents() dead code remains — cosmetic, not blocking.

🚫 REQUEST CHANGES

Summary: The delta commit is correct and addresses the module-level state reset concern. However, the context config fallback path issue (lines 361 and 996) raised in prior reviews remains unresolved. This is a bug that will cause context configs with opaque CUIDs to generate incorrectly named files and broken import paths. Please address before merging.

Discarded (0)

No new findings to discard in this delta review.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
orchestrator-only 0 0 0 0 3 0

Note: Delta review — no subagent reviewers dispatched for a 1-line change that was already analyzed in prior reviews.

@github-actions github-actions bot deleted a comment from claude bot Mar 2, 2026
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) New Issues | (0) Pending | Risk: Low

Delta Review: 844541000 — "feedback"

The latest commit addresses all three critical issues raised in prior reviews:

1. Context config fallback paths now use buildComponentFileName()

  • collectContextConfigRecords() at line ~365 — ✅ Fixed
  • collectContextTemplateReferences() at line ~1005 — ✅ Fixed

Both locations now extract contextConfigName and pass it through buildComponentFileName(contextConfigId, contextConfigName), ensuring context configs with opaque CUIDs generate human-readable file names like my-context-config-lo2ddxn2.ts instead of raw UUIDs.

2. getImportStem() now warns on missing stems

  • Added _importStemsActive flag (line 159) that tracks whether stems have been populated
  • When getImportStem() is called with a non-human-readable ID that isn't registered, it now emits a console.warn() (lines 171-174)
  • This provides early feedback during inkeep pull rather than deferring failures to TypeScript compile time

Implementation quality:

  • The warning only fires when stems are active AND the ID appears to be a CUID/UUID (via isHumanReadableId(id) check), avoiding spurious warnings for intentionally human-readable IDs
  • The fallback to raw ID is preserved for backward compatibility with existing workflows

No new issues introduced by this delta.

Prior Feedback Status

Prior Issue Status
Context config fallback at line ~360 ✅ Resolved in this commit
Context config fallback at line ~995 ✅ Resolved in this commit
Silent fallback in getImportStem() ✅ Resolved — now warns
Module-level state reset ✅ Already resolved in prior commit (setImportStems({}) at line 136)
Test coverage for setImportStems()/getImportStem() 💭 Not blocking — existing integration tests exercise the paths
registerAllComponents() dead code 💭 Cosmetic — not blocking, could be removed in follow-up

✅ APPROVE

Summary: All critical issues from prior reviews have been addressed. The two-pass collect-then-generate architecture is clean, the context config paths now correctly use buildComponentFileName(), and getImportStem() provides helpful warnings for missing stems. The implementation is ready to ship. 🚀

Discarded (0)

No findings to discard — delta is clean.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
orchestrator-only 0 0 0 0 0 0

Note: Delta review — all prior critical issues were addressed. No subagent reviewers dispatched for a focused 2-file delta that directly resolves previously-identified bugs.

@github-actions github-actions bot deleted a comment from claude bot Mar 2, 2026
@itoqa
Copy link

itoqa bot commented Mar 2, 2026

Ito Test Report ✅

18 test cases ran. 18 passed.

This test run verified the new human-readable import stem functionality for the inkeep pull CLI command (PR #2468). The changes introduce getImportStem, setImportStems, and buildComponentFileName functions that produce human-readable file names and import paths instead of raw opaque CUIDs. All 18 test cases passed, confirming that the full vitest test suite (517 tests) passes without regressions, TypeScript type-checking is clean, core logic functions behave correctly, and edge/adversarial cases are handled gracefully. The two-pass generation approach correctly collects all components first, builds a stem map, then generates files with proper import paths.

✅ Passed (18)
Test Case Summary Timestamp Screenshot
ROUTE-1 Full vitest test suite executed: 49 test files passed, 517 tests passed, 45 skipped, 0 failures 1:12 ROUTE-1_1-12.png
ROUTE-2 TypeScript typecheck completed successfully with zero type errors 1:52 ROUTE-2_1-52.png
LOGIC-1 setImportStems registered opaque CUID with stem, getImportStem returned correct stem 2:40 LOGIC-1_2-40.png
LOGIC-2 getImportStem returns human-readable ID unchanged both when stems inactive and active 2:57 LOGIC-2_2-57.png
LOGIC-3 Generation integration tests passed - 2 tests verifying two-pass generation with correct import paths 3:53 LOGIC-3_3-53.png
LOGIC-4 Component registry verified - utils.test.ts 24 tests passed, registerAllComponents uses buildComponentFileName 5:01 LOGIC-4_5-01.png
LOGIC-5 After setImportStems({}), _importStemsActive is false, getImportStem returns raw opaque ID without warning 3:15 LOGIC-5_3-15.png
EDGE-1 buildComponentFileName returns full ID.ts when name is undefined and ID is opaque 6:10 EDGE-1_6-10.png
EDGE-2 When name produces empty kebab, buildComponentFileName falls back to full ID.ts 6:19 EDGE-2_6-19.png
EDGE-3 buildComponentFileName returns 'support-agent.ts' because isHumanReadableId is true 6:25 EDGE-3_6-25.png
EDGE-4 getImportStem emits console.warn for unregistered opaque ID when stems are active 6:32 EDGE-4_6-32.png
EDGE-5 ??= operator in map building keeps first value when same ID appears twice 6:41 EDGE-5_6-41.png
EDGE-6 buildComponentFileName handles null name correctly via !name guard 6:48 EDGE-6_6-48.png
ADV-1 Path traversal ID produces filename with path chars surviving, getImportStem returns registered stem 8:09 ADV-1_8-09.png
ADV-2 1000-char name produces 1012-char filename without crashing, no length truncation 8:20 ADV-2_8-20.png
ADV-3 JS reserved word 'Class' handled correctly - stems are path segments not variable names 8:27 ADV-3_8-27.png
ADV-4 Empty string ID with name produces 'some-name-.ts' without crashing 8:34 ADV-4_8-34.png
ADV-5 Sequential setImportStems calls correctly replace entire map - last call wins 8:41 ADV-5_8-41.png
📋 View Recording

Screen Recording

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) New Issues | (0) Pending | Risk: Low

Delta Review: f13bab72d — Merge from main

This is a delta review scoped to changes since the last automated review (commit 844541000).

Delta contents:

  • Merge commit from main branch
  • Unrelated PRs merged to main: playground conversation reset fix (#2473), SigNoz error logging (#2469), version packages (#2453)

No changes to the actual PR files (agents-cli/src/commands/pull-v4/*) since the last review.

Prior Review Status

The previous review at commit 844541000 gave an ✅ APPROVE with all critical issues addressed:

Prior Issue Status
Context config fallback at line ~360 ✅ Resolved
Context config fallback at line ~995 ✅ Resolved
Silent fallback in getImportStem() ✅ Resolved — now warns
Module-level state reset ✅ Resolved
Test coverage for new functions 💭 Not blocking
registerAllComponents() dead code 💭 Cosmetic — follow-up

No new issues introduced by this delta.


✅ APPROVE

Summary: The delta contains only a merge from main with unrelated changes — no modifications to the PR's actual implementation. The prior review's approval stands. The two-pass collect-then-generate architecture is clean, context config paths correctly use buildComponentFileName(), and getImportStem() provides helpful warnings for missing stems. Ready to ship! 🚀

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
orchestrator-only 0 0 0 0 0 0

Note: Delta review with no PR file changes — no subagent reviewers dispatched.

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) New Issues | (0) Pending | Risk: Low

Delta Review: 92f36e703 — Merge from main

This is a delta review scoped to changes since the last automated review (commit f13bab72d).

Delta contents:

  • Merge commit from main branch
  • Unrelated PRs merged to main: copilot token fix (#2491), docker-compose updates (#2490), scheduled trigger timezone (#2487), email integration (#2038), stepper component (#2480), API key restriction (#2478), version packages

No changes to the actual PR files (agents-cli/src/commands/pull-v4/*) since the last review.

Prior Review Status

The previous review at commit f13bab72d gave an ✅ APPROVE with all critical issues addressed:

Prior Issue Status
Context config fallback at line ~360 ✅ Resolved
Context config fallback at line ~995 ✅ Resolved
Silent fallback in getImportStem() ✅ Resolved — now warns
Module-level state reset ✅ Resolved
Test coverage for new functions 💭 Not blocking
registerAllComponents() dead code 💭 Cosmetic — follow-up

No new issues introduced by this delta.


✅ APPROVE

Summary: The delta contains only a merge from main with unrelated changes — no modifications to the PR's actual implementation. The prior review's approval stands. The two-pass collect-then-generate architecture is clean, context config paths correctly use buildComponentFileName(), and getImportStem() provides helpful warnings for missing stems. Ready to ship! 🎉

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
orchestrator-only 0 0 0 0 0 0

Note: Delta review with no PR file changes — no subagent reviewers dispatched.

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