guardian-framework 0.1.73 → 0.1.74

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/dist/cli.js CHANGED
@@ -3197,7 +3197,7 @@ __export(exports_package, {
3197
3197
  bin: () => bin,
3198
3198
  author: () => author
3199
3199
  });
3200
- var name = "guardian-framework", version = "0.1.73", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
3200
+ var name = "guardian-framework", version = "0.1.74", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
3201
3201
  var init_package = __esm(() => {
3202
3202
  exports = {
3203
3203
  ".": {
package/dist/exports.js CHANGED
@@ -1982,7 +1982,7 @@ __export(exports_package, {
1982
1982
  bin: () => bin,
1983
1983
  author: () => author
1984
1984
  });
1985
- var name = "guardian-framework", version = "0.1.73", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1985
+ var name = "guardian-framework", version = "0.1.74", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1986
1986
  var init_package = __esm(() => {
1987
1987
  exports = {
1988
1988
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.73",
3
+ "version": "0.1.74",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -105,6 +105,47 @@ export function createRemoteIssue(
105
105
  return { success: false, issueNumber: null, error: "Could not parse issue number" };
106
106
  }
107
107
 
108
+ // Create a real GitLab Epic via the glab CLI.
109
+ // Returns the epic IID (string) on success, null on failure.
110
+ export function createGitlabEpic(
111
+ cwd: string,
112
+ repository: string,
113
+ title: string,
114
+ description: string,
115
+ ): string | null {
116
+ // Derive group from repository path: "group/subgroup/project" -> "group/subgroup"
117
+ const parts = repository.split("/");
118
+ if (parts.length < 2) return null;
119
+ const group = parts.slice(0, -1).join("/");
120
+
121
+ // Build description from markdown content (strip YAML frontmatter)
122
+ const desc = description.replace(/^---[\s\S]*?---\n*/, "").trim();
123
+
124
+ // Create epic via glab CLI
125
+ const result = runScript(
126
+ cwd,
127
+ `glab epic create --group "${group}" --title "${title}" --description "${desc.slice(0, 5000)}" 2>&1`,
128
+ );
129
+
130
+ if (result.exitCode !== 0) {
131
+ // Fallback: try the API directly
132
+ const body = JSON.stringify({ title, description: desc.slice(0, 5000) });
133
+ const apiResult = runScript(
134
+ cwd,
135
+ `glab api --method POST "groups/${group}/epics" --body '${body}' 2>/dev/null | jq -r '.iid // empty'`,
136
+ );
137
+ if (apiResult.exitCode !== 0 || !apiResult.stdout.trim()) {
138
+ console.warn(`Failed to create GitLab epic: ${result.stdout || apiResult.stdout}`);
139
+ return null;
140
+ }
141
+ return apiResult.stdout.trim();
142
+ }
143
+
144
+ // Parse epic IID from output: "https://gitlab.com/groups/.../-/epics/42"
145
+ const epicMatch = result.stdout.match(/epics\/(\d+)/);
146
+ return epicMatch ? epicMatch[1] : null;
147
+ }
148
+
108
149
  // Ensure the GitHub/GitLab repository exists and local git remote is configured.
109
150
  // Returns the repository slug if remote is ready, empty string if not available.
110
151
  export function ensureRemoteRepo(
@@ -28,6 +28,7 @@ import {
28
28
  readRepoTool,
29
29
  runScript,
30
30
  saveRoadmapState,
31
+ createGitlabEpic,
31
32
  } from "./architect-lib/helpers";
32
33
  import {
33
34
  generateArchitectureReadinessMarkdown,
@@ -306,25 +307,48 @@ class EpicManager {
306
307
  if (tdd) {
307
308
  const language = readLanguage(this.cwd);
308
309
  if (isTddSupported(language)) {
309
- tddTestFiles = generateEpicTestFiles({
310
+ tddTestFiles.push(...generateEpicTestFiles({
310
311
  components: slice.nextLogicalSlice,
311
312
  moduleId: slice.module,
312
313
  cwd: this.cwd,
313
314
  language,
314
- });
315
+ }));
316
+ }
317
+ }
318
+
319
+ // ── GitLab Epic creation (only for GitLab repos with remote access) ──
320
+ let effectiveEpicId: string | null = null;
321
+ if (repoTool === "glab" && hasRemote && remoteRepo) {
322
+ const epicTitle = `Epic: ${name} — ${slice.module}`;
323
+ const epicDesc = [
324
+ `# ${epicTitle}`,
325
+ "",
326
+ `**Module:** ${slice.module}`,
327
+ `**Components:** ${slice.nextLogicalSlice.length}`,
328
+ "",
329
+ "## Issues",
330
+ ...issues.map((i) => `- ${i.title}${i.remoteIssueId ? ` (#${i.remoteIssueId})` : ""}`),
331
+ "",
332
+ "---",
333
+ "Auto-generated by Guardian Architect",
334
+ ].join("\n");
335
+ effectiveEpicId = createGitlabEpic(this.cwd, remoteRepo, epicTitle, epicDesc);
336
+ if (effectiveEpicId) {
337
+ console.warn(`Created GitLab epic #${effectiveEpicId}`);
315
338
  }
316
339
  }
317
340
 
318
341
  const state: EpicState = {
319
342
  name,
320
343
  trackingIssueId: effectiveTrackingId,
321
- epicId: null,
344
+ epicId: effectiveEpicId,
322
345
  slices: [slice],
323
346
  issues,
324
347
  status: "planning",
325
348
  currentIssueIndex: 0,
326
349
  createdAt: new Date().toISOString(),
327
350
  tdd,
351
+ tddTestFiles,
328
352
  };
329
353
 
330
354
  this.state = state;
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-13T11:44:15Z",
2
+ "timestamp": "2026-07-13T11:51:20Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {
@@ -163,7 +163,7 @@ case "$PLATFORM" in
163
163
 
164
164
  # Try to add labels separately (non-fatal)
165
165
  if [[ -n "$LABELS" && -n "$ISSUE_NUMBER" ]]; then
166
- glab issue update "$ISSUE_NUMBER" --label "$LABELS" 2>/dev/null || true
166
+ glab issue update "$ISSUE_NUMBER" --label "$LABELS" ${REPO:+--repo "$REPO"} 2>/dev/null || true
167
167
  fi
168
168
 
169
169
  echo "TRACKING_ID=$ISSUE_NUMBER"
@@ -57,7 +57,7 @@ Relates to #$EPIC_ID" 2>/dev/null
57
57
  # API: POST /groups/:group/epics/:epic_iid/issues/:issue_iid
58
58
  # Derive group from repository path: "group/subgroup/project" -> "group/subgroup"
59
59
  if [[ -n "$REPO" ]]; then
60
- local group_path=$(echo "$REPO" | rev | cut -d'/' -f2- | rev)
60
+ group_path=$(echo "$REPO" | rev | cut -d'/' -f2- | rev)
61
61
  if [[ -n "$group_path" ]]; then
62
62
  glab api --method POST "groups/$group_path/epics/$EPIC_ID/issues/$ISSUE_ID" 2>/dev/null && {
63
63
  echo "Linked GitLab issue #$ISSUE_ID to epic #$EPIC_ID"