@loopstack/git-examples 0.1.1 → 0.1.2

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
@@ -38,6 +38,42 @@ npm install @loopstack/git-examples
38
38
  import { GitExamplesModule } from '@loopstack/git-examples';
39
39
  ```
40
40
 
41
+ ## Required app-module configuration
42
+
43
+ Both examples invoke git tools that execute on a **remote agent**, so the workspace needs a connected `sandbox` environment. `GitExamplesModule` declares the slot via `RemoteClientModule.forFeature(...)`, but the consumer must register at least one matching environment in their root module via `RemoteClientModule.forRoot(...)`:
44
+
45
+ ```typescript
46
+ import { Module } from '@nestjs/common';
47
+ import { GitExamplesModule } from '@loopstack/git-examples';
48
+ import { LoopstackModule } from '@loopstack/loopstack-module';
49
+ import { RemoteClientModule } from '@loopstack/remote-client';
50
+
51
+ @Module({
52
+ imports: [
53
+ LoopstackModule.forRoot(),
54
+ RemoteClientModule.forRoot({
55
+ environments: {
56
+ available: [
57
+ {
58
+ type: 'sandbox',
59
+ name: 'Local Remote Server',
60
+ connectionUrl: process.env.SANDBOX_URL ?? 'http://localhost:3080',
61
+ agentUrl: process.env.SANDBOX_AGENT_URL ?? 'http://localhost:3001',
62
+ local: true,
63
+ },
64
+ ],
65
+ },
66
+ }),
67
+ GitExamplesModule,
68
+ ],
69
+ })
70
+ export class AppModule {}
71
+ ```
72
+
73
+ The `type: 'sandbox'` must match the slot type declared by `GitExamplesModule`. The simplest remote agent option is `@loopstack/remote-server` on `localhost:3001`.
74
+
75
+ `GitExamplesModule` re-imports `GitModule.forFeature()` and `GitHubIntegrationModule`, which transitively brings in the global `OAuthModule` and the GitHub OAuth provider — no additional OAuth wiring required in your AppModule. You just need to set the GitHub OAuth client env vars (see [Environment](#environment) below).
76
+
41
77
  ## Environment
42
78
 
43
79
  The repo-sync example requires GitHub OAuth credentials:
@@ -12,7 +12,7 @@ type ConnectResult = z.infer<typeof ConnectResultSchema>;
12
12
  export declare class GithubRepoSyncExampleWorkflow extends BaseWorkflow {
13
13
  private readonly connectGitHubWorkflow;
14
14
  constructor(connectGitHubWorkflow: ConnectGitHubWorkflow);
15
- connect(_state: Record<string, unknown>): Promise<void>;
15
+ connect(): Promise<void>;
16
16
  connectComplete(_state: Record<string, unknown>, input: TransitionInput<ConnectResult>): Promise<void>;
17
17
  }
18
18
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"github-repo-sync-example.workflow.d.ts","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAA0C,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,QAAA,MAAM,mBAAmB;;;;;mBAGvB,CAAC;AACH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEzD,qBAKa,6BAA8B,SAAQ,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBAArB,qBAAqB,EAAE,qBAAqB;IAKnE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAavC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC;CAa7F"}
1
+ {"version":3,"file":"github-repo-sync-example.workflow.d.ts","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAA0C,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,QAAA,MAAM,mBAAmB;;;;;mBAGvB,CAAC;AACH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEzD,qBAKa,6BAA8B,SAAQ,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBAArB,qBAAqB,EAAE,qBAAqB;IAKnE,OAAO;IAaP,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC;CAa7F"}
@@ -23,7 +23,7 @@ let GithubRepoSyncExampleWorkflow = class GithubRepoSyncExampleWorkflow extends
23
23
  super();
24
24
  this.connectGitHubWorkflow = connectGitHubWorkflow;
25
25
  }
26
- async connect(_state) {
26
+ async connect() {
27
27
  await this.connectGitHubWorkflow.run({}, { callback: { transition: 'connectComplete' }, show: 'inline', label: 'Connecting to GitHub...' });
28
28
  }
29
29
  async connectComplete(_state, input) {
@@ -43,7 +43,7 @@ exports.GithubRepoSyncExampleWorkflow = GithubRepoSyncExampleWorkflow;
43
43
  __decorate([
44
44
  (0, common_1.Transition)({ to: 'connecting' }),
45
45
  __metadata("design:type", Function),
46
- __metadata("design:paramtypes", [Object]),
46
+ __metadata("design:paramtypes", []),
47
47
  __metadata("design:returntype", Promise)
48
48
  ], GithubRepoSyncExampleWorkflow.prototype, "connect", null);
49
49
  __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"github-repo-sync-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAyF;AAEzF,sEAAsE;AAEtE,MAAM,mBAAmB,GAAG,OAAC,CAAC,KAAK,CAAC;IAClC,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC/C,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,OAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;CACzC,CAAC,CAAC;AAQI,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,qBAAY;IAChC;IAA7B,YAA6B,qBAA4C;QACvE,KAAK,EAAE,CAAC;QADmB,0BAAqB,GAArB,qBAAqB,CAAuB;IAEzE,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAC,MAA+B;QAC3C,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAClC,EAAE,EACF,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAClG,CAAC;IACJ,CAAC;IAQK,AAAN,KAAK,CAAC,eAAe,CAAC,MAA+B,EAAE,KAAqC;QAC1F,IAAI,WAAW,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;gBAC9C,QAAQ,EAAE,0CAA0C;aACrD,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;YAC9C,QAAQ,EAAE,2CAA2C,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI;SAC5F,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAwC,CAAC,CAAC;IACvG,CAAC;CACF,CAAA;AAhCY,sEAA6B;AAMlC;IADL,IAAA,mBAAU,EAAC,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC;;;;4DAMhC;AAQK;IANL,IAAA,mBAAU,EAAC;QACV,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,mBAAmB;KAC5B,CAAC;;;;oEAaD;wCA/BU,6BAA6B;IALzC,IAAA,iBAAQ,EAAC;QACR,KAAK,EAAE,gCAAgC;QACvC,WAAW,EACT,+LAA+L;KAClM,CAAC;qCAEoD,0CAAqB;GAD9D,6BAA6B,CAgCzC"}
1
+ {"version":3,"file":"github-repo-sync-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/github-repo-sync/github-repo-sync-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAyF;AAEzF,sEAAsE;AAEtE,MAAM,mBAAmB,GAAG,OAAC,CAAC,KAAK,CAAC;IAClC,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC/C,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,OAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;CACzC,CAAC,CAAC;AAQI,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,qBAAY;IAChC;IAA7B,YAA6B,qBAA4C;QACvE,KAAK,EAAE,CAAC;QADmB,0BAAqB,GAArB,qBAAqB,CAAuB;IAEzE,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAClC,EAAE,EACF,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAClG,CAAC;IACJ,CAAC;IAQK,AAAN,KAAK,CAAC,eAAe,CAAC,MAA+B,EAAE,KAAqC;QAC1F,IAAI,WAAW,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;gBAC9C,QAAQ,EAAE,0CAA0C;aACrD,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,yBAAgB,EAAE;YAC9C,QAAQ,EAAE,2CAA2C,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI;SAC5F,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAwC,CAAC,CAAC;IACvG,CAAC;CACF,CAAA;AAhCY,sEAA6B;AAMlC;IADL,IAAA,mBAAU,EAAC,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC;;;;4DAMhC;AAQK;IANL,IAAA,mBAAU,EAAC;QACV,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,mBAAmB;KAC5B,CAAC;;;;oEAaD;wCA/BU,6BAA6B;IALzC,IAAA,iBAAQ,EAAC;QACR,KAAK,EAAE,gCAAgC;QACvC,WAAW,EACT,+LAA+L;KAClM,CAAC;qCAEoD,0CAAqB;GAD9D,6BAA6B,CAgCzC"}
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "github",
10
10
  "workflow"
11
11
  ],
12
- "version": "0.1.1",
12
+ "version": "0.1.2",
13
13
  "license": "MIT",
14
14
  "author": {
15
15
  "name": "Jakob Klippel",
@@ -33,7 +33,7 @@
33
33
  "@loopstack/common": "*",
34
34
  "@loopstack/git-module": "*",
35
35
  "@loopstack/github-integration": "*",
36
- "@nestjs/common": "^11.1.19",
36
+ "@nestjs/common": "^12.0.1",
37
37
  "zod": "^4.3.6"
38
38
  },
39
39
  "files": [
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { GitModule } from '@loopstack/git-module';
3
+ import { RemoteClientModule } from '@loopstack/remote-client';
4
+ import { replay, runWorkflow } from '@loopstack/testing';
5
+ import { GitExamplesModule } from '../../../git-examples.module';
6
+ import { GitCommitFlowExampleWorkflow } from '../git-commit-flow-example.workflow';
7
+
8
+ /**
9
+ * A scripted multi-tool pipeline with no LLM. The git and write tools are HTTP-backed (they
10
+ * talk to a remote workspace), so the whole flow runs against the tool boundary: each tool's
11
+ * response is replayed in call order, and the workflow's own state threading and document
12
+ * rendering run for real. Envelopes match each tool's `resultSchema`.
13
+ *
14
+ * `RemoteClientModule.forRoot()` establishes the global remote-client services the git tools
15
+ * depend on; the tools' actual network calls are replaced by the replay script.
16
+ */
17
+ describe('GitCommitFlowExampleWorkflow', () => {
18
+ it('writes a file, then stages, commits, and reads back the log', async () => {
19
+ const run = await runWorkflow(GitCommitFlowExampleWorkflow, undefined, {
20
+ imports: [
21
+ RemoteClientModule.forRoot(),
22
+ GitModule.forFeature(),
23
+ RemoteClientModule.forFeature({ slots: [{ id: 'sandbox', type: 'sandbox', title: 'Sandbox' }] }),
24
+ GitExamplesModule,
25
+ ],
26
+ replay: replay({
27
+ version: 3,
28
+ recordings: [
29
+ { tool: 'write', envelope: { data: { success: true, path: 'tmp/example.txt' } } },
30
+ {
31
+ tool: 'git_status',
32
+ envelope: {
33
+ data: { branch: 'main', staged: [], modified: [], untracked: ['tmp/example.txt'], deleted: [] },
34
+ },
35
+ },
36
+ { tool: 'git_add', envelope: { data: { success: true } } },
37
+ { tool: 'git_commit', envelope: { data: { hash: 'abc123', message: 'chore: example commit' } } },
38
+ {
39
+ tool: 'git_log',
40
+ envelope: {
41
+ data: {
42
+ commits: [
43
+ {
44
+ hash: 'abc123',
45
+ shortHash: 'abc123',
46
+ message: 'chore: example commit',
47
+ author: 'Test',
48
+ date: '2026-01-01',
49
+ },
50
+ ],
51
+ },
52
+ },
53
+ },
54
+ ],
55
+ }),
56
+ });
57
+
58
+ expect(run.error).toBeUndefined();
59
+ expect(run.status).toBe('completed');
60
+ expect(run.path).toEqual(['writeTestFile', 'checkStatus', 'stageAll', 'commit', 'readBack']);
61
+
62
+ const texts = run.documents.map((d) => (d.content as { text?: string }).text ?? '');
63
+ expect(texts.some((t) => t.includes('"branch": "main"'))).toBe(true);
64
+ expect(texts.some((t) => t.includes('chore: example commit'))).toBe(true);
65
+ });
66
+ });
@@ -20,7 +20,7 @@ export class GithubRepoSyncExampleWorkflow extends BaseWorkflow {
20
20
  }
21
21
 
22
22
  @Transition({ to: 'connecting' })
23
- async connect(_state: Record<string, unknown>) {
23
+ async connect() {
24
24
  await this.connectGitHubWorkflow.run(
25
25
  {},
26
26
  { callback: { transition: 'connectComplete' }, show: 'inline', label: 'Connecting to GitHub...' },