forgedock 1.0.9 → 1.0.11
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 +84 -0
- package/bin/forgedock.mjs +537 -5627
- package/bin/init-detect.mjs +1 -1
- package/bin/init-enrich-api.mjs +20 -2
- package/bin/tui.mjs +60 -43
- package/commands/forgedock-init.md +498 -317
- package/commands/work-on.md +19 -0
- package/docs/CONFIG.md +37 -0
- package/docs/FORGE-PROTOCOL.md +740 -0
- package/docs/anthropic-devrel-outreach.md +197 -0
- package/docs/articles/devto-how-i-built-an-ai-agent-that-never-forgets.md +165 -0
- package/docs/demo.tape +113 -0
- package/docs/distribution-submissions.md +181 -0
- package/docs/launch/reddit-r-claudeai.md +83 -0
- package/docs/launch/reddit-r-devops.md +80 -0
- package/docs/launch/reddit-r-localllama.md +86 -0
- package/docs/launch/reddit-r-programming.md +81 -0
- package/docs/launch/reddit-r-sideproject.md +84 -0
- package/docs/launch/show-hn.md +84 -0
- package/docs/site/command-reference.md +423 -0
- package/docs/site/forge-annotation-protocol.md +266 -0
- package/docs/site/getting-started.md +168 -0
- package/docs/site/how-it-works.md +191 -0
- package/docs/site/index.md +59 -0
- package/docs/site/vs-manual-workflows.md +167 -0
- package/docs/twitter-launch-thread.md +303 -0
- package/docs/youtube-demo-script.md +297 -0
- package/package.json +1 -1
package/bin/init-detect.mjs
CHANGED
package/bin/init-enrich-api.mjs
CHANGED
|
@@ -107,8 +107,26 @@ export function parseEnrichedDraft(output, draft) {
|
|
|
107
107
|
// Found the closing brace of the top-level object — parse the candidate.
|
|
108
108
|
try {
|
|
109
109
|
const enriched = JSON.parse(output.slice(start, i + 1));
|
|
110
|
-
// Basic sanity check: must have the required top-level sections
|
|
111
|
-
|
|
110
|
+
// Basic sanity check: must have the required top-level sections and
|
|
111
|
+
// each section must be a plain non-null object. The typeof+null guard
|
|
112
|
+
// rejects null and primitives; Array.isArray() additionally rejects
|
|
113
|
+
// arrays since typeof [] === 'object' would otherwise pass them.
|
|
114
|
+
const isPlainObject = (v) =>
|
|
115
|
+
typeof v === "object" && v !== null && !Array.isArray(v);
|
|
116
|
+
if (
|
|
117
|
+
!isPlainObject(enriched.project) ||
|
|
118
|
+
!isPlainObject(enriched.paths) ||
|
|
119
|
+
!isPlainObject(enriched.branches) ||
|
|
120
|
+
!isPlainObject(enriched.meta)
|
|
121
|
+
) {
|
|
122
|
+
if (process.env.FORGEDOCK_DEBUG) {
|
|
123
|
+
const failing = ["project", "paths", "branches", "meta"]
|
|
124
|
+
.filter((k) => !isPlainObject(enriched[k]))
|
|
125
|
+
.join(", ");
|
|
126
|
+
console.error(
|
|
127
|
+
` ${dim("[debug]")} api enrichment: parseEnrichedDraft rejected LLM output — missing/invalid sections: ${failing}. Falling back to baseline draft.`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
112
130
|
return draft;
|
|
113
131
|
}
|
|
114
132
|
return enriched;
|
package/bin/tui.mjs
CHANGED
|
@@ -106,9 +106,6 @@ export function box(content, { title = "", padding = 1, width } = {}) {
|
|
|
106
106
|
const lines = Array.isArray(content) ? content : String(content).split("\n");
|
|
107
107
|
const pad = " ".repeat(padding);
|
|
108
108
|
|
|
109
|
-
// Strip ANSI codes to compute visual width
|
|
110
|
-
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
111
|
-
|
|
112
109
|
const contentWidth = lines.reduce(
|
|
113
110
|
(max, l) => Math.max(max, stripAnsi(l).length),
|
|
114
111
|
0,
|
|
@@ -659,8 +656,6 @@ export function table(
|
|
|
659
656
|
) {
|
|
660
657
|
if (!rows || rows.length === 0) return "";
|
|
661
658
|
|
|
662
|
-
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
663
|
-
|
|
664
659
|
// Compute column widths
|
|
665
660
|
const colCount = Math.max(...rows.map((r) => r.length));
|
|
666
661
|
const widths = Array.from({ length: colCount }, (_, c) =>
|
|
@@ -701,6 +696,66 @@ export function table(
|
|
|
701
696
|
return lines.join("\n") + "\n";
|
|
702
697
|
}
|
|
703
698
|
|
|
699
|
+
// ---------------------------------------------------------------------------
|
|
700
|
+
// ANSI string utilities (exported for testing)
|
|
701
|
+
// ---------------------------------------------------------------------------
|
|
702
|
+
|
|
703
|
+
/** Strip all ANSI CSI sequences from a string, returning only visible text. */
|
|
704
|
+
export function stripAnsi(s) {
|
|
705
|
+
return s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Truncate an ANSI-decorated string to at most `maxWidth` visible characters,
|
|
710
|
+
* never bisecting an escape sequence.
|
|
711
|
+
*
|
|
712
|
+
* Tokens from the cut region are suppressed — only ANSI tokens encountered
|
|
713
|
+
* while the visible budget is still live are forwarded to the output.
|
|
714
|
+
* A trailing reset (\x1b[0m) is appended when truncation occurs and the
|
|
715
|
+
* result already contains ANSI sequences, preventing color bleed into
|
|
716
|
+
* adjacent columns.
|
|
717
|
+
*/
|
|
718
|
+
export function truncateVisible(str, maxWidth) {
|
|
719
|
+
const ansiRe = /\x1b\[[0-9;]*[A-Za-z]/g;
|
|
720
|
+
let visible = 0;
|
|
721
|
+
let result = "";
|
|
722
|
+
let lastIndex = 0;
|
|
723
|
+
let m;
|
|
724
|
+
ansiRe.lastIndex = 0;
|
|
725
|
+
while ((m = ansiRe.exec(str)) !== null) {
|
|
726
|
+
// Consume plain-text chars before this ANSI token
|
|
727
|
+
const plain = str.slice(lastIndex, m.index);
|
|
728
|
+
const remaining = maxWidth - visible;
|
|
729
|
+
if (remaining > 0) {
|
|
730
|
+
result += plain.slice(0, remaining);
|
|
731
|
+
visible += Math.min(plain.length, remaining);
|
|
732
|
+
}
|
|
733
|
+
// Only include the ANSI token while the visible budget is not yet exhausted.
|
|
734
|
+
// Once the cut point is reached, tokens from the removed region are suppressed;
|
|
735
|
+
// the post-loop reset guard below handles color-state cleanup.
|
|
736
|
+
if (visible < maxWidth) {
|
|
737
|
+
result += m[0];
|
|
738
|
+
}
|
|
739
|
+
lastIndex = ansiRe.lastIndex;
|
|
740
|
+
}
|
|
741
|
+
// Remaining plain text after last ANSI token
|
|
742
|
+
const remaining = maxWidth - visible;
|
|
743
|
+
if (remaining > 0) {
|
|
744
|
+
result += str.slice(lastIndex, lastIndex + remaining);
|
|
745
|
+
}
|
|
746
|
+
// If truncation occurred (or visible length exactly equals maxWidth) and the
|
|
747
|
+
// result contains any ANSI sequences, append a full reset to prevent color from
|
|
748
|
+
// bleeding into adjacent columns. The guard uses >= rather than > to cover the
|
|
749
|
+
// exact-boundary case: when visible === maxWidth the loop guard (visible < maxWidth)
|
|
750
|
+
// suppresses the original trailing RESET token, so the post-loop guard must fire
|
|
751
|
+
// to close any open sequences. Inputs shorter than maxWidth are unaffected because
|
|
752
|
+
// stripAnsi(str).length < maxWidth makes the condition false.
|
|
753
|
+
if (stripAnsi(str).length >= maxWidth && result.includes("\x1b[")) {
|
|
754
|
+
result += "\x1b[0m";
|
|
755
|
+
}
|
|
756
|
+
return result;
|
|
757
|
+
}
|
|
758
|
+
|
|
704
759
|
// ---------------------------------------------------------------------------
|
|
705
760
|
// Annotated Review Screen
|
|
706
761
|
// ---------------------------------------------------------------------------
|
|
@@ -821,50 +876,12 @@ export async function annotatedReviewScreen(
|
|
|
821
876
|
};
|
|
822
877
|
}
|
|
823
878
|
|
|
824
|
-
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
825
|
-
|
|
826
879
|
// Pad an ANSI-decorated string to `width` visible characters.
|
|
827
880
|
function padVisible(str, width) {
|
|
828
881
|
const visual = stripAnsi(str).length;
|
|
829
882
|
return str + " ".repeat(Math.max(0, width - visual));
|
|
830
883
|
}
|
|
831
884
|
|
|
832
|
-
// Truncate an ANSI-decorated string to at most `maxWidth` visible characters,
|
|
833
|
-
// never bisecting an escape sequence.
|
|
834
|
-
function truncateVisible(str, maxWidth) {
|
|
835
|
-
const ansiRe = /\x1b\[[0-9;]*[A-Za-z]/g;
|
|
836
|
-
let visible = 0;
|
|
837
|
-
let result = "";
|
|
838
|
-
let lastIndex = 0;
|
|
839
|
-
let m;
|
|
840
|
-
ansiRe.lastIndex = 0;
|
|
841
|
-
while ((m = ansiRe.exec(str)) !== null) {
|
|
842
|
-
// Consume plain-text chars before this ANSI token
|
|
843
|
-
const plain = str.slice(lastIndex, m.index);
|
|
844
|
-
const remaining = maxWidth - visible;
|
|
845
|
-
if (remaining > 0) {
|
|
846
|
-
result += plain.slice(0, remaining);
|
|
847
|
-
visible += Math.min(plain.length, remaining);
|
|
848
|
-
}
|
|
849
|
-
// Always include the ANSI token (zero visible width)
|
|
850
|
-
result += m[0];
|
|
851
|
-
lastIndex = ansiRe.lastIndex;
|
|
852
|
-
}
|
|
853
|
-
// Remaining plain text after last ANSI token
|
|
854
|
-
const remaining = maxWidth - visible;
|
|
855
|
-
if (remaining > 0) {
|
|
856
|
-
result += str.slice(lastIndex, lastIndex + remaining);
|
|
857
|
-
}
|
|
858
|
-
// If truncation occurred and the result contains any ANSI sequences,
|
|
859
|
-
// append a full reset to prevent color from bleeding into adjacent columns.
|
|
860
|
-
// Truncation is detected by checking whether the original string has more
|
|
861
|
-
// visible characters than maxWidth (i.e. stripAnsi(str).length > maxWidth).
|
|
862
|
-
if (stripAnsi(str).length > maxWidth && result.includes("\x1b[")) {
|
|
863
|
-
result += "\x1b[0m";
|
|
864
|
-
}
|
|
865
|
-
return result;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
885
|
// ── Render the annotated table ────────────────────────────────────────────
|
|
869
886
|
function renderScreen() {
|
|
870
887
|
process.stdout.write("\n");
|