glab-agent 0.2.5 โ†’ 0.2.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glab-agent",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "description": "Multi-agent GitLab To-Do watcher with YAML-defined agents, skills, and GitLab registry.",
6
6
  "license": "MIT",
@@ -95,6 +95,10 @@ export interface GitlabClient {
95
95
  getWikiPage(projectId: number | string, slug: string): Promise<WikiPage>;
96
96
  createWikiPage(projectId: number | string, title: string, content: string): Promise<WikiPage>;
97
97
  updateWikiPage(projectId: number | string, slug: string, title: string, content: string): Promise<WikiPage>;
98
+ getProject(projectIdOrPath: number | string): Promise<{ id: number } | undefined>;
99
+ createProject(name: string, options?: { visibility?: string; initializeWithReadme?: boolean }): Promise<{ id: number }>;
100
+ getRepositoryFile(projectId: number | string, filePath: string, ref?: string): Promise<{ content: string } | undefined>;
101
+ createOrUpdateRepositoryFile(projectId: number | string, filePath: string, content: string, commitMessage: string, options?: { create?: boolean }): Promise<void>;
98
102
  }
99
103
 
100
104
  interface GitlabGlabClientOptions {
@@ -693,6 +697,58 @@ export class GitlabGlabClient implements GitlabClient {
693
697
  };
694
698
  }
695
699
 
700
+ async getProject(projectIdOrPath: number | string): Promise<{ id: number } | undefined> {
701
+ try {
702
+ const encoded = typeof projectIdOrPath === "string" ? encodeURIComponent(projectIdOrPath) : projectIdOrPath;
703
+ const payload = await this.readJson(`projects/${encoded}`);
704
+ const p = (payload ?? {}) as Payload;
705
+ const id = Number(p.id);
706
+ return Number.isNaN(id) ? undefined : { id };
707
+ } catch {
708
+ return undefined;
709
+ }
710
+ }
711
+
712
+ async createProject(name: string, options?: { visibility?: string; initializeWithReadme?: boolean }): Promise<{ id: number }> {
713
+ const stdout = await this.request("projects", {
714
+ method: "POST",
715
+ fields: {
716
+ name,
717
+ path: name,
718
+ visibility: options?.visibility ?? "public",
719
+ initialize_with_readme: options?.initializeWithReadme ? "true" : "false"
720
+ }
721
+ });
722
+ const payload = JSON.parse(stdout) as Payload;
723
+ return { id: Number(payload.id) };
724
+ }
725
+
726
+ async getRepositoryFile(projectId: number | string, filePath: string, ref?: string): Promise<{ content: string } | undefined> {
727
+ try {
728
+ const encoded = typeof projectId === "string" ? encodeURIComponent(projectId) : projectId;
729
+ const endpoint = this.withQuery(`projects/${encoded}/repository/files/${encodeURIComponent(filePath)}`, { ref: ref ?? "main" });
730
+ const payload = await this.readJson(endpoint);
731
+ const p = (payload ?? {}) as Payload;
732
+ const content = typeof p.content === "string" ? Buffer.from(p.content, "base64").toString("utf8") : "";
733
+ return { content };
734
+ } catch {
735
+ return undefined;
736
+ }
737
+ }
738
+
739
+ async createOrUpdateRepositoryFile(projectId: number | string, filePath: string, content: string, commitMessage: string, options?: { create?: boolean }): Promise<void> {
740
+ const encoded = typeof projectId === "string" ? encodeURIComponent(projectId) : projectId;
741
+ const method = options?.create ? "POST" : "PUT";
742
+ await this.request(`projects/${encoded}/repository/files/${encodeURIComponent(filePath)}`, {
743
+ method,
744
+ fields: {
745
+ branch: "main",
746
+ content,
747
+ commit_message: commitMessage
748
+ }
749
+ });
750
+ }
751
+
696
752
  /**
697
753
  * Direct HTTP call using Node's built-in fetch.
698
754
  * Used for endpoints where glab CLI's response parsing has issues (e.g. PUT /user/status).
@@ -882,6 +882,81 @@ async function publishAgentProfileWiki(
882
882
  }
883
883
  }
884
884
 
885
+ async function publishProfileReadme(
886
+ config: LocalAgentConfig,
887
+ dependencies: WatcherDependencies
888
+ ): Promise<void> {
889
+ const logger = dependencies.logger ?? console;
890
+ const def = config.agentDefinition;
891
+ const username = config.botUsername;
892
+ if (!def || !username) return;
893
+
894
+ const projectPath = `${username}/${username}`;
895
+
896
+ // Build README content
897
+ const displayName = def.display_name || def.name;
898
+ const lines: string[] = [
899
+ `## ๐Ÿ‘‹ Hi, I'm ${displayName}`,
900
+ "",
901
+ ];
902
+
903
+ if (def.description) {
904
+ lines.push(def.description, "");
905
+ }
906
+
907
+ const skillNames = def.skill_refs.length > 0 ? def.skill_refs : def.skills.map(s => s.name);
908
+ if (skillNames.length > 0) {
909
+ lines.push("### ๐Ÿ›  Skills", "", ...skillNames.map(s => `- ${s}`), "");
910
+ }
911
+
912
+ if (def.prompt.preamble) {
913
+ lines.push("### ๐Ÿ“‹ What I Do", "", def.prompt.preamble.trim(), "");
914
+ }
915
+
916
+ lines.push(
917
+ "### ๐Ÿ’ฌ How to Use",
918
+ "",
919
+ `ๅœจไปปๆ„ issue ๆˆ– MR ไธญ่ฏ„่ฎบ \`@${username} <ไฝ ็š„้œ€ๆฑ‚>\`๏ผŒๆˆ‘ไผš่‡ชๅŠจๆŽฅๅ•ๅค„็†ใ€‚`,
920
+ "",
921
+ "### ๐Ÿ“Š Status",
922
+ "",
923
+ `- Provider: \`${def.provider}\``,
924
+ `- Poll interval: ${def.poll_interval_seconds ?? 30}s`,
925
+ "",
926
+ "---",
927
+ `*Auto-updated by [glab-agent](https://www.npmjs.com/package/glab-agent)*`,
928
+ );
929
+
930
+ const content = lines.join("\n");
931
+
932
+ try {
933
+ // Check if profile project exists
934
+ let project = await dependencies.gitlabClient.getProject(projectPath);
935
+
936
+ if (!project) {
937
+ // Create profile project
938
+ project = await dependencies.gitlabClient.createProject(username, {
939
+ visibility: "public",
940
+ initializeWithReadme: true
941
+ });
942
+ logger.info(`Created profile project: ${projectPath}`);
943
+ }
944
+
945
+ // Update or create README
946
+ const existing = await dependencies.gitlabClient.getRepositoryFile(project.id, "README.md");
947
+ await dependencies.gitlabClient.createOrUpdateRepositoryFile(
948
+ project.id,
949
+ "README.md",
950
+ content,
951
+ "Update profile README",
952
+ { create: !existing }
953
+ );
954
+ logger.info(`Profile README updated: ${projectPath}`);
955
+ } catch (error) {
956
+ logger.info?.(`Profile README update skipped: ${String(error).slice(0, 150)}`);
957
+ }
958
+ }
959
+
885
960
  export async function markAgentOffline(
886
961
  _config: LocalAgentConfig,
887
962
  gitlabClient: GitlabClient,
@@ -1264,6 +1339,7 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<void
1264
1339
  // Update GitLab user bio with agent profile (P1: Agent = ๅ‘˜ๅทฅ)
1265
1340
  await updateAgentUserBio(config, dependencies);
1266
1341
  await publishAgentProfileWiki(config, dependencies);
1342
+ await publishProfileReadme(config, dependencies);
1267
1343
  await updateAgentUserStatus(dependencies, "idle", undefined, config);
1268
1344
  } catch (error) {
1269
1345
  logger.error(`Token validation failed for agent "${agentName}": ${String(error)}`);