djournal 0.2.0 → 0.3.1
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.
|
@@ -42,7 +42,8 @@ native payloads to these names.
|
|
|
42
42
|
|
|
43
43
|
- Hooks are read-only.
|
|
44
44
|
- `stop_hook_active: true` always permits stopping.
|
|
45
|
-
- `closed` markers must resolve
|
|
46
|
-
|
|
45
|
+
- `closed` markers must resolve to an existing Markdown spine entry under the
|
|
46
|
+
resolved journal root; `.journal/...` paths use `.djournal.json` when a
|
|
47
|
+
global project store is configured.
|
|
47
48
|
- Adapters do not infer task meaning from file changes or unstable transcripts.
|
|
48
49
|
- Adding a harness must not change `AUTOMATION.md` semantics.
|
|
@@ -169,7 +169,7 @@ function handle(payload, options = {}) {
|
|
|
169
169
|
if (match[1].toLowerCase() === "closed" && !validateClosedPath(root, match[2])) {
|
|
170
170
|
return {
|
|
171
171
|
decision: "block",
|
|
172
|
-
reason: "The journal-status closed marker must reference an existing
|
|
172
|
+
reason: "The journal-status closed marker must reference an existing Markdown spine entry under the resolved journal root. In global-store projects, .journal/... paths resolve through .djournal.json.",
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
if (match[1].toLowerCase() === "closed") {
|
package/README.md
CHANGED
package/bin/journal.js
CHANGED
|
@@ -25,7 +25,7 @@ function usage() {
|
|
|
25
25
|
journal status [--target DIR] [--json]
|
|
26
26
|
journal doctor [--target DIR] [--json]
|
|
27
27
|
journal config [--target DIR] [KEY [VALUE]] [--dry-run] [--json]
|
|
28
|
-
journal share [--target DIR] [--work SLUG] [--dry-run] [--json]
|
|
28
|
+
journal share [--target DIR] [--work SLUG | --all] [--dry-run] [--json]
|
|
29
29
|
journal sync [--target DIR] [--work SLUG] [--dry-run] [--json]
|
|
30
30
|
|
|
31
31
|
Options:
|
|
@@ -34,7 +34,7 @@ Options:
|
|
|
34
34
|
--json Emit JSON
|
|
35
35
|
--work SLUG Select a journal work item instead of active state
|
|
36
36
|
--harness LIST Comma-separated codex,claude-code selection
|
|
37
|
-
--all Select or
|
|
37
|
+
--all Select all work items for share, or every harness
|
|
38
38
|
--instructions-only Install core instructions without harness hooks
|
|
39
39
|
`;
|
|
40
40
|
}
|
|
@@ -72,6 +72,10 @@ function parseArgs(argv) {
|
|
|
72
72
|
else throw new InstallerError(`unknown option: ${arg}`, "USAGE");
|
|
73
73
|
}
|
|
74
74
|
if (options.all && options.harnesses.length) throw new InstallerError("use either --all or --harness", "USAGE");
|
|
75
|
+
if (options.all && options.work) throw new InstallerError("use either --all or --work", "USAGE");
|
|
76
|
+
if (options.all && !["install", "uninstall", "share"].includes(command)) {
|
|
77
|
+
throw new InstallerError(`--all is not supported for ${command}`, "USAGE");
|
|
78
|
+
}
|
|
75
79
|
if (options.instructionsOnly && (options.all || options.harnesses.length)) {
|
|
76
80
|
throw new InstallerError("--instructions-only cannot be combined with harness selection", "USAGE");
|
|
77
81
|
}
|
|
@@ -92,6 +96,12 @@ function print(value, json) {
|
|
|
92
96
|
if (value.harnesses) process.stdout.write(`harnesses: ${value.harnesses.join(", ") || "instructions-only"}\n`);
|
|
93
97
|
if (value.key) process.stdout.write(`${value.key}: ${JSON.stringify(value.value)}\n`);
|
|
94
98
|
else if (value.config) process.stdout.write(`${JSON.stringify(value.config, null, 2)}\n`);
|
|
99
|
+
if (value.workItems) {
|
|
100
|
+
for (const item of value.workItems) {
|
|
101
|
+
process.stdout.write(`${item.changed ? "shared" : "unchanged"} ${item.work}\n`);
|
|
102
|
+
if (item.warning) process.stdout.write(`warning: ${item.warning}\n`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
95
105
|
if (value.conflicts?.length) process.stdout.write(`conflicts: ${value.conflicts.join(", ")}\n`);
|
|
96
106
|
if (value.files) {
|
|
97
107
|
for (const file of value.files) process.stdout.write(`${file.status.padEnd(8)} ${file.path}\n`);
|
|
@@ -28,6 +28,16 @@ Mark a specific work item as shared:
|
|
|
28
28
|
djournal share --work 2026-07-03-01-git-backed-journal-collaboration
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Mark every current work item as shared:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
djournal share --all
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Already-shared work keeps its original sharing record. `--all` marks only work
|
|
38
|
+
items that currently exist in the canonical project store; it does not change
|
|
39
|
+
the default for future work.
|
|
40
|
+
|
|
31
41
|
Dry run:
|
|
32
42
|
|
|
33
43
|
```bash
|
package/lib/installer/index.js
CHANGED
|
@@ -556,6 +556,19 @@ function readActiveWork(context, requested) {
|
|
|
556
556
|
return { slug, relative, file, text, metadata, visibility: metadata.visibility || "local_only" };
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
function readAllWork(context) {
|
|
560
|
+
const workRoot = path.join(context.journalRoot, "work");
|
|
561
|
+
const entries = fs.existsSync(workRoot) ? fs.readdirSync(workRoot, { withFileTypes: true }) : [];
|
|
562
|
+
const workItems = entries
|
|
563
|
+
.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink())
|
|
564
|
+
.map((entry) => entry.name)
|
|
565
|
+
.filter((slug) => fs.existsSync(path.join(workRoot, slug, "work.md")))
|
|
566
|
+
.sort()
|
|
567
|
+
.map((slug) => readActiveWork(context, slug));
|
|
568
|
+
if (!workItems.length) throw new InstallerError("no journal work items found", "NO_ACTIVE_WORK");
|
|
569
|
+
return workItems;
|
|
570
|
+
}
|
|
571
|
+
|
|
559
572
|
function readJournalConfig(target) {
|
|
560
573
|
const context = projectContext(target);
|
|
561
574
|
return readProjectConfig(context);
|
|
@@ -638,29 +651,43 @@ function share(options) {
|
|
|
638
651
|
const target = checkTarget(options.target || process.cwd());
|
|
639
652
|
const context = projectContext(target, options);
|
|
640
653
|
const config = readProjectConfig(context);
|
|
641
|
-
const work = readActiveWork(context, options.work);
|
|
642
654
|
const projectionRoot = projectionRootFor(target, config);
|
|
643
|
-
const ignored = config.sync?.mode === "colocated" ? gitIgnored(projectionRoot, `.journal/work/${work.slug}`, options) : false;
|
|
644
|
-
const warning = ignored
|
|
645
|
-
? `.journal/work/${work.slug} appears to be ignored by Git; update .gitignore or force-add it before expecting colocated Git commits to share this work.`
|
|
646
|
-
: undefined;
|
|
647
655
|
const shared = sharedWorkItems(config);
|
|
648
|
-
|
|
649
|
-
|
|
656
|
+
const workItems = options.all ? readAllWork(context) : [readActiveWork(context, options.work)];
|
|
657
|
+
const sharedAt = new Date().toISOString();
|
|
658
|
+
const sharedBy = process.env.JOURNAL_USER_ID || runGitConfigEmail(options) || `${os.userInfo().username}@local`;
|
|
659
|
+
const nextShared = { ...shared };
|
|
660
|
+
const results = workItems.map((work) => {
|
|
661
|
+
const ignored = config.sync?.mode === "colocated" ? gitIgnored(projectionRoot, `.journal/work/${work.slug}`, options) : false;
|
|
662
|
+
const warning = ignored
|
|
663
|
+
? `.journal/work/${work.slug} appears to be ignored by Git; update .gitignore or force-add it before expecting colocated Git commits to share this work.`
|
|
664
|
+
: undefined;
|
|
665
|
+
const changed = !Object.prototype.hasOwnProperty.call(shared, work.slug);
|
|
666
|
+
if (changed) nextShared[work.slug] = { sharedAt, sharedBy };
|
|
667
|
+
return { work: work.slug, changed, shared: true, gitIgnored: ignored, warning };
|
|
668
|
+
});
|
|
669
|
+
const changed = results.some((result) => result.changed);
|
|
670
|
+
if (!options.all) {
|
|
671
|
+
const result = results[0];
|
|
672
|
+
if (!changed) return { action: "share", target, projectKey: context.projectKey, ...result };
|
|
650
673
|
}
|
|
651
674
|
const next = mergePlainObject(config, {
|
|
652
675
|
sharing: {
|
|
653
|
-
sharedWorkItems:
|
|
654
|
-
...shared,
|
|
655
|
-
[work.slug]: {
|
|
656
|
-
sharedAt: new Date().toISOString(),
|
|
657
|
-
sharedBy: process.env.JOURNAL_USER_ID || runGitConfigEmail(options) || `${os.userInfo().username}@local`,
|
|
658
|
-
},
|
|
659
|
-
},
|
|
676
|
+
sharedWorkItems: nextShared,
|
|
660
677
|
},
|
|
661
678
|
});
|
|
662
|
-
if (!options.dryRun) writeProjectConfig(context, next);
|
|
663
|
-
return { action: "share", target, projectKey: context.projectKey,
|
|
679
|
+
if (changed && !options.dryRun) writeProjectConfig(context, next);
|
|
680
|
+
if (!options.all) return { action: "share", target, projectKey: context.projectKey, ...results[0], dryRun: !!options.dryRun };
|
|
681
|
+
return {
|
|
682
|
+
action: "share",
|
|
683
|
+
target,
|
|
684
|
+
projectKey: context.projectKey,
|
|
685
|
+
all: true,
|
|
686
|
+
changed,
|
|
687
|
+
shared: true,
|
|
688
|
+
workItems: results,
|
|
689
|
+
dryRun: !!options.dryRun,
|
|
690
|
+
};
|
|
664
691
|
}
|
|
665
692
|
|
|
666
693
|
function runGitConfigEmail(options = {}) {
|