guardian-framework 0.1.69 → 0.1.71

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.69", 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.71", 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.69", 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.71", 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.69",
3
+ "version": "0.1.71",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -7,11 +7,16 @@ export function generateIssueMarkdown(
7
7
  slice: ArchitectureSlice,
8
8
  issueIndex: number,
9
9
  totalIssues: number,
10
+ tdd?: boolean,
10
11
  ): string {
11
12
  const moduleId = slice.module.replace(/^module-/, "");
12
13
  const componentName = component.name.toLowerCase().replace(/\s+/g, "-");
13
14
  const issueId = `ISSUE-${moduleId.toUpperCase()}-${issueIndex + 1}`;
14
15
 
16
+ const testLine = tdd
17
+ ? ` - "update: tests/unit/${moduleId}/${componentName}/ (failing tests already generated — make them pass)"`
18
+ : ` - "create: tests/unit/${moduleId}/${componentName}/"`;
19
+
15
20
  return `---
16
21
  guardian_issue:
17
22
  id: "${issueId}"
@@ -70,7 +75,7 @@ ${component.dependencies.map((d) => ` - "${d}"`).join("\n")}
70
75
  - "modify: src/${moduleId}/application/"
71
76
  - "modify: src/${moduleId}/infrastructure/"
72
77
  - "modify: src/${moduleId}/interfaces/"
73
- - "create: tests/unit/${moduleId}/${componentName}/"
78
+ - ${testLine}
74
79
  - "create: tests/integration/${moduleId}/${componentName}/"
75
80
  ---
76
81
 
@@ -147,6 +147,14 @@ class EpicManager {
147
147
 
148
148
  ctx.ui.setStatus("architect", `Planning epic: ${name}`);
149
149
 
150
+ // Ensure git repo is initialized before any remote operations
151
+ const gitCheck = runScript(this.cwd, "git rev-parse --git-dir 2>/dev/null");
152
+ if (gitCheck.exitCode !== 0) {
153
+ runScript(this.cwd, "git init");
154
+ runScript(this.cwd, "git add .");
155
+ runScript(this.cwd, 'git commit -m "Initial Guardian scaffold"');
156
+ }
157
+
150
158
  const repoTool = readRepoTool(this.cwd);
151
159
  const repository = readRepository(this.cwd);
152
160
  const targetRepo = repository || slice.module;
@@ -159,18 +167,11 @@ class EpicManager {
159
167
  repoTool === "glab" ? "glab auth status 2>/dev/null" : "gh auth status 2>/dev/null",
160
168
  );
161
169
  if (authCheck.exitCode === 0) {
162
- // Verify project-level access, not just global auth
163
- const projectAccessCheck = repository ? runScript(
164
- this.cwd,
165
- repoTool === "glab"
166
- ? `glab api "projects/${repository}" --method GET 2>/dev/null | jq -r '.id // empty'`
167
- : `gh api "repos/${repository}" --method GET 2>/dev/null | jq -r '.id // empty'`,
168
- ) : { exitCode: 0, stdout: "" };
169
-
170
- if (projectAccessCheck.exitCode === 0 && projectAccessCheck.stdout.trim().length > 0) {
171
- remoteRepo = ensureRemoteRepo(this.cwd, targetRepo, name, repoTool);
172
- hasRemote = remoteRepo.length > 0;
173
- }
170
+ // Try to ensure remote repo — ensureRemoteRepo handles both:
171
+ // - existing repos: configures local origin
172
+ // - nonexistent repos: creates remote repo via gh/glab CLI
173
+ remoteRepo = ensureRemoteRepo(this.cwd, targetRepo, name, repoTool);
174
+ hasRemote = remoteRepo.length > 0;
174
175
  }
175
176
  }
176
177
 
@@ -250,7 +251,7 @@ class EpicManager {
250
251
  status: "planned" as string,
251
252
  remoteIssueId: null as string | null,
252
253
  };
253
- const md = generateIssueMarkdown(comp, slice, i, slice.nextLogicalSlice.length);
254
+ const md = generateIssueMarkdown(comp, slice, i, slice.nextLogicalSlice.length, tdd);
254
255
  writeFileSync(join(issuesDir, `${id}.md`), md);
255
256
  if (hasRemote && remoteRepo) {
256
257
  const result = createRemoteIssue(this.cwd, entry.title, join(issuesDir, `${id}.md`), "epic,implementation", remoteRepo);
@@ -757,15 +758,7 @@ export default function (pi: ExtensionAPI) {
757
758
  return;
758
759
  }
759
760
 
760
- // Initialize git if needed
761
- try {
762
- const gitCheck = runScript(ctx.cwd, "git rev-parse --git-dir 2>/dev/null");
763
- if (gitCheck.exitCode !== 0) {
764
- runScript(ctx.cwd, "git init");
765
- runScript(ctx.cwd, "git add .");
766
- runScript(ctx.cwd, 'git commit -m "Initial Guardian scaffold"');
767
- }
768
- } catch { /* ignore */ }
761
+
769
762
 
770
763
  // Remove stale pipeline state so the new one takes effect
771
764
  try {
@@ -20,7 +20,7 @@
20
20
  * /pipeline abort Kill pipeline
21
21
  */
22
22
 
23
- import { execSync } from "node:child_process";
23
+ import { execFileSync, execSync } from "node:child_process";
24
24
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
25
25
  import { dirname, join } from "node:path";
26
26
  import {
@@ -863,14 +863,71 @@ export default function (pi: ExtensionAPI) {
863
863
  const prevItemIndex = state.currentItemIndex;
864
864
  const prevStepIndex = state.currentStepIndex;
865
865
  const stepName = (params.stepName as string) || state.steps[prevStepIndex]?.name;
866
+
867
+ // Verify current step before advancing (unless user explicitly provides the step name,
868
+ // which counts as manual confirmation)
869
+ if (!params.stepName) {
870
+ const verification = manager.verifyCurrentStep();
871
+ if (!verification.verified) {
872
+ return {
873
+ content: [{ type: "text" as const, text: `Cannot advance: ${verification.reason}` }],
874
+ };
875
+ }
876
+ }
877
+
866
878
  manager.markStepPassed(stepName);
867
879
  manager.advanceStep();
868
880
 
869
881
  // Re-read state after advance
870
882
  const updatedState = manager.getState()!;
871
883
 
884
+ // Non-blocking: sync progress to tracking issue if one exists
885
+ try {
886
+ const trackingStatePath = join(ctx.cwd, ".pi/.guardian-epic-state.json");
887
+ if (existsSync(trackingStatePath)) {
888
+ const trackingState = JSON.parse(readFileSync(trackingStatePath, "utf-8")) as {
889
+ trackingIssueId?: string | null;
890
+ issues?: { id: string; title: string; status: string }[];
891
+ };
892
+ if (trackingState.trackingIssueId && !trackingState.trackingIssueId.startsWith("local:")) {
893
+ const stepNote = stepName
894
+ ? `✓ Step "${stepName}" complete for item "${updatedState.items[prevItemIndex]}"`
895
+ : `✓ Advanced to item ${updatedState.currentItemIndex + 1}/${updatedState.items.length}: "${updatedState.items[updatedState.currentItemIndex]}"`;
896
+ const updateScript = join(ctx.cwd, ".pi/scripts/git/update-tracking-issue.sh");
897
+ if (existsSync(updateScript)) {
898
+ execFileSync("bash", [updateScript, "--id", trackingState.trackingIssueId, "--comment", stepNote], {
899
+ cwd: ctx.cwd,
900
+ timeout: 30_000,
901
+ encoding: "utf-8",
902
+ stdio: "ignore",
903
+ });
904
+ }
905
+ }
906
+ }
907
+ } catch { /* non-blocking — don't fail the pipeline */ }
908
+
872
909
  // Pipeline complete
873
910
  if (updatedState.currentItemIndex >= updatedState.items.length) {
911
+ // Non-blocking: close tracking issue and epic on pipeline completion
912
+ try {
913
+ const epicStatePath = join(ctx.cwd, ".pi/.guardian-epic-state.json");
914
+ if (existsSync(epicStatePath)) {
915
+ const epicState = JSON.parse(readFileSync(epicStatePath, "utf-8")) as {
916
+ trackingIssueId?: string | null;
917
+ epicId?: string | null;
918
+ };
919
+ if (epicState.trackingIssueId && !epicState.trackingIssueId.startsWith("local:")) {
920
+ const closeEpicScript = join(ctx.cwd, ".pi/scripts/git/close-epic.sh");
921
+ if (existsSync(closeEpicScript)) {
922
+ const args: string[] = [closeEpicScript, "--tracking-id", epicState.trackingIssueId];
923
+ if (epicState.epicId) args.push("--epic-id", epicState.epicId);
924
+ execFileSync("bash", args, {
925
+ cwd: ctx.cwd, timeout: 30_000, encoding: "utf-8", stdio: "ignore",
926
+ });
927
+ }
928
+ }
929
+ }
930
+ } catch { /* non-blocking */ }
874
931
  return {
875
932
  content: [{
876
933
  type: "text" as const,
@@ -884,6 +941,30 @@ export default function (pi: ExtensionAPI) {
884
941
  const movedToNextItem = updatedState.currentItemIndex !== prevItemIndex;
885
942
 
886
943
  // If we moved to a new item (completed all steps of previous item),
944
+ // close its remote issue
945
+ if (movedToNextItem) {
946
+ // Close remote issue for the completed item
947
+ try {
948
+ const epicStatePath = join(ctx.cwd, ".pi/.guardian-epic-state.json");
949
+ if (existsSync(epicStatePath)) {
950
+ const epicState = JSON.parse(readFileSync(epicStatePath, "utf-8")) as {
951
+ issues?: { id: string; remoteIssueId?: string | null }[];
952
+ };
953
+ const prevItemId = updatedState.items[prevItemIndex];
954
+ const prevIssue = epicState.issues?.find((i) => i.id === prevItemId);
955
+ if (prevIssue?.remoteIssueId) {
956
+ const closeScript = join(ctx.cwd, ".pi/scripts/git/close-issue.sh");
957
+ if (existsSync(closeScript)) {
958
+ execFileSync("bash", [closeScript, "--id", prevIssue.remoteIssueId], {
959
+ cwd: ctx.cwd, timeout: 30_000, encoding: "utf-8", stdio: "ignore",
960
+ });
961
+ }
962
+ }
963
+ }
964
+ } catch { /* non-blocking */ }
965
+ }
966
+
967
+ // If we moved to a new item and the next step is implement,
887
968
  // inject the full next-task prompt with issue context
888
969
  if (movedToNextItem && currentStep?.name === "implement") {
889
970
  // Find the remote issue ID from epic state
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-12T15:32:11Z",
2
+ "timestamp": "2026-07-13T11:15:40Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {
@@ -56,7 +56,7 @@ if [[ "$PLATFORM" == "none" ]]; then
56
56
  fi
57
57
 
58
58
  # Determine branch name from issue ID
59
- BRANCH_NAME="feature/${ISSUE_ID}"
59
+ BRANCH_NAME="feat/${ISSUE_ID}"
60
60
 
61
61
  # Ensure we're on the main branch before creating feature branch
62
62
  git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" 2>/dev/null || true
@@ -36,7 +36,7 @@ while [[ $# -gt 0 ]]; do
36
36
  done
37
37
 
38
38
  PLATFORM=$(detect_platform)
39
- BRANCH_NAME="feature/${ISSUE_ID}"
39
+ BRANCH_NAME="feat/${ISSUE_ID}"
40
40
 
41
41
  if [[ "$PLATFORM" == "none" ]]; then
42
42
  echo "No git platform detected. Cannot merge without gh/glab CLI."